Özel bir yöntemi çağırmanın bir yolu var mı?


153

S'yi başka bir sınıfa döndürmek için XML ve yansıma kullanan Objectbir sınıfım var.

Normalde bu nesneler harici bir nesnenin alt alanlarıdır, ancak bazen anında oluşturmak istediğim bir şeydir. Böyle bir şey denedim ama işe yaramadı. Bunun nedeni, Java'nın privateyansıtma yöntemlerine erişmenize izin vermemesidir .

Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");

if ("SomeObject".equals(objectName))
    object = someObject;
else
    object = this;

method = object.getClass().getMethod(methodName, (Class[]) null);

Sağlanan yöntem ise private, bir NoSuchMethodException. Metodu publicyaparak veya onu türetmek için başka bir sınıf yaparak çözebilirim .

Uzun lafın kısası, privatedüşünme yoluyla bir yönteme erişmenin bir yolu olup olmadığını merak ediyordum .

Yanıtlar:


311

Özel yöntemi yansıtma ile çağırabilirsiniz. Gönderilen kodun son bitini değiştirme:

Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);

Birkaç uyarı var. İlk olarak, süper türlerden miras getDeclaredMethodalınmayan, yalnızca akımda bildirilen yöntemi bulacaktır Class. Bu nedenle, gerekirse somut sınıf hiyerarşisini aşın. İkinci olarak, a yöntemin SecurityManagerkullanılmasını engelleyebilir setAccessible. Bu nedenle, bir PrivilegedAction(kullanarak AccessControllerveya Subject) olarak çalışması gerekebilir .


2
Bunu geçmişte yaptığımda, yöntemi çağırdıktan sonra method.setAccessible (false) olarak da çağırdım, ancak bunun gerekli olup olmadığı hakkında hiçbir fikrim yok.
shsteimer

16
Hayır, erişilebilirliği ayarladığınızda, yalnızca o örnek için geçerlidir. Bu belirli Method nesnesinin kontrolünüzden kaçmasına izin vermediğin sürece güvenlidir.
erickson

7
Öyleyse, sınıf dışından çağrılabiliyorsa özel yöntemlere sahip olmanın anlamı nedir?
Peter Ajtai

3
Ayrıca getDeclaredMethod()sadece aramak yerine çağırdığınızdan da emin olun getMethod()- bu özel yöntemler için çalışmaz.
Ercksen

4
@PeterAjtai Geç yanıt için özür dilerim, ancak şu şekilde düşünün: Günümüzde çoğu insan, kilidin önemsiz bir şekilde kırılabileceğini veya tamamen atlatılabileceğini bilseler bile kapılarını kilitliyor. Neden? Çünkü çoğunlukla dürüst insanları dürüst tutmaya yardımcı olur. privateErişimin de benzer bir rol oynadığını düşünebilirsiniz .
erickson


26

Yöntem ilkel olmayan veri türünü kabul ederse, aşağıdaki yöntem herhangi bir sınıfın özel bir yöntemini çağırmak için kullanılabilir:

public static Object genericInvokeMethod(Object obj, String methodName,
            Object... params) {
        int paramCount = params.length;
        Method method;
        Object requiredObj = null;
        Class<?>[] classArray = new Class<?>[paramCount];
        for (int i = 0; i < paramCount; i++) {
            classArray[i] = params[i].getClass();
        }
        try {
            method = obj.getClass().getDeclaredMethod(methodName, classArray);
            method.setAccessible(true);
            requiredObj = method.invoke(obj, params);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return requiredObj;
    }

Kabul edilen Parametreler obj, methodName ve parametrelerdir. Örneğin

public class Test {
private String concatString(String a, String b) {
    return (a+b);
}
}

Yöntem concatString şu şekilde çağrılabilir:

Test t = new Test();
    String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");

7
ParamCount neden gerekli? Sadece params.length kullanamaz mısın ?
Saad Malik


3

Yansıtma yoluyla yürütme korumalı yöntemler için eksiksiz kod sağlamama izin verin. Jenerikler, otomatik kutulu parametreler ve boş değerler dahil her türlü parametreyi destekler

@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}

public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass(), instance, methodName, params);
}

@SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {

    Method[] allMethods = clazz.getDeclaredMethods();

    if (allMethods != null && allMethods.length > 0) {

        Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);

        for (Method method : allMethods) {
            String currentMethodName = method.getName();
            if (!currentMethodName.equals(methodName)) {
                continue;
            }
            Type[] pTypes = method.getParameterTypes();
            if (pTypes.length == paramClasses.length) {
                boolean goodMethod = true;
                int i = 0;
                for (Type pType : pTypes) {
                    if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
                        goodMethod = false;
                        break;
                    }
                }
                if (goodMethod) {
                    method.setAccessible(true);
                    return (T) method.invoke(instance, params);
                }
            }
        }

        throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
            Arrays.toString(paramClasses));
    }

    throw new MethodNotFoundException("There are no methods found with name " + methodName);
}

Yöntem, otomatik kutulanmış parametrelerin uyumluluğunu kontrol etmek için apache ClassUtils kullanır


90000'den fazla görüş ve kabul edilmiş bir cevaba sahip 9 yaşındaki bir soruyu cevaplamanın kesinlikle bir anlamı yok. Bunun yerine cevaplanmamış soruları yanıtlayın.
Anuraag Baishya

1

Bir varyant daha çok güçlü JOOR kitaplığı kullanıyor https://github.com/jOOQ/jOOR

MyObject myObject = new MyObject()
on(myObject).get("privateField");  

Son statik sabitler gibi herhangi bir alanı değiştirmeye ve kalıtım hiyerarşisinde somut bir sınıf belirtmeden korumalı yöntemleri çağırmaya izin verir.

<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
     <groupId>org.jooq</groupId>
     <artifactId>joor-java-8</artifactId>
     <version>0.9.7</version>
</dependency>

0

Doğrudan, tür güvenli Java yansıması için Manifold'un @Jailbreakini kullanabilirsiniz :

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}

@Jailbreakhiyerarşisindeki footüm üyelere doğrudan erişim için derleyicideki yerel değişkenin kilidini açar Foo.

Benzer şekilde , tek seferlik kullanım için jailbreak () uzantı yöntemini kullanabilirsiniz:

foo.jailbreak().callMe();

Bu jailbreak()yöntem aracılığıyla, Foohiyerarşisindeki herhangi bir üyeye erişebilirsiniz .

Her iki durumda da, derleyici yöntem çağrısını sizin için güvenli bir şekilde, genel bir yöntemmiş gibi çözer, Manifold ise sizin için etkili yansıma kodu üretir.

Alternatif olarak, tür statik olarak bilinmiyorsa, bir türün uygulamasını bildirmek zorunda kalmadan tatmin edebileceği bir arabirimi tanımlamak için Yapısal Yazma'yı kullanabilirsiniz . Bu strateji, tür güvenliğini korur ve yansıtma ve proxy koduyla ilişkili performans ve kimlik sorunlarını önler.

Manifold hakkında daha fazlasını keşfedin .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.