如何访问第三方库中的受保护Java方法?

14
假设您必须访问在代码中其他位置接收到的Java对象的受保护方法。您的解决方案是什么?
我知道一种方法:您可以使用反射并在Method对象上调用setAccessible(true)。
还有其他想法吗?

你是否正在使用拥有已发布API的库?如果是,将该信息添加到问题中;这样你会得到更好的帮助。 - Atreys
这不是我第一次遇到这个问题,但如果你好奇的话:Eclipse Debug Plugin,JDILocalVariable,getStackFrame。网址:http://www.docjar.com/docs/api/org/eclipse/jdt/internal/debug/core/model/JDILocalVariable.html - salman.mirghasemi
8个回答

10
根据Java访问修饰符,除了扩展对象(如果您收到对象,则无法这样做),还可以从与您收到的对象位于同一包中的对象中访问它。因此,您的选项是在相同包中创建一个包装类,以通过受保护的方法为您检索属性。

6
您可以创建一个公共方法,调用受保护的方法并返回结果来子类化该方法。
如果无法这样做(如果类是final),那么setAccessible几乎是您唯一的方法。

考虑到您已经接收到该对象,您不能强制转换该对象,对吗? - salman.mirghasemi
我猜你的意思是将其转换为另一种类型。不,你不能在运行时将对象类型(类)转换为另一个对象类型。你可以将它包装在另一个对象中,但是受保护访问的问题仍然存在。此外,如果你的对象是使用Spring或Hibernate或任何其他使用运行时代理的框架生成的,也不能保证反射会起作用。目标代理将不包含在代理类中不公开的任何方法。 - pap

4

另一种选择是创建一个继承第三方类的类,该类具有您感兴趣的受保护方法。

public class ThirdPartyClass
{
   protected void foo(){}
}

并且
public MyClass extends ThirdPartyClass
{

     public void callFoo()
     {
           foo();
     }

}

2
考虑到您已经接收到该对象,您不能强制转换该对象,对吗? - salman.mirghasemi
@salman,你在编译时有第三方类的访问权限吗? - Bala R

4
你可以扩展该类,重写方法,并使重写的方法为公共方法。然后让它只调用super.method()即可。

1
考虑到您已经接收到该对象,您不能强制转换该对象,对吗? - salman.mirghasemi

3
另一种方法是扩展类(如果可能的话),通过继承获取对受保护方法的访问权限。如果您不创建对象,则不可能实现此操作,因为您需要在编译时进行,并且您需要自己创建对象。
一个不太可靠的解决方案是使用组合。因此,您可以在同一包中创建一个类,例如OtherObjectWrapper。由于它在同一包中,您可以通过公共API调用对象的受保护方法。但是,这并不推荐,因为您不拥有要添加类的包,这可能会使您的代码非常脆弱。
package com.foo;

public class OtherObjectWrapper {
   private com.foo.OtherObject wrappedObject;

   public OtherObjectWrapper(com.foo.OtherObject wrappedObject) {
     this.wrappedObject = wrappedObject;
   }

   public void callTheProtectedMethod() {
     wrappedObject.callTheProtectedMethod();
   }
}

考虑一下API设计者在将方法标记为protected时的想法。也许他们不知道自己在做什么,应该将其设置为public,或者更糟糕的是应该设置为包私有或完全私有。或者他们知道,并且决定只有在同一包中或通过继承的代码才能访问受保护的方法。如果它是受保护的,可能有一个原因,所以要小心,因为你可能会将你的代码行为与可能改变并破坏你的代码的行为联系起来。还要查看谁拥有第三方对象以及是否有更好的API来访问受保护方法的功能。

考虑到您已经接收到该对象,您不能强制转换该对象,对吗? - salman.mirghasemi

2
如果您能将调用类放在同一个包中,则可以访问该方法。这和从那个类继承是访问受保护方法的唯一两种非反射方式。

请注意,调用类应由与第三方类相同的类加载器加载,请参见 https://dev59.com/FFHTa4cB1Zd3GeqPSIb3 - axtavt

2

如前所述,子类化通常是访问该方法的标准方式。其他方法(在同一包装器中,反射)通常不应使用,因为如果您无法扩展类(由于是最终的),则经常有很好的理由使访问该方法变得困难。

如果库的质量还算不错,您绝对不应该使用除子类化以外的任何其他手段来访问受保护的方法或根本不访问该方法。


0
如果一个类不是final的,你可以使用匿名类来调用它的protected方法:
new ClassWithProtectedMethod() {
    @Override
    protected void method() {
        super.method();
    }
}.method();

请注意,将method()设为public是不必要的(因为新的匿名类与原类在同一个包中)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接