如何从其他包中的类访问包私有类?

Pra*_*ate 8 java access-specifier

我有以下课程

Hello.java

package speak.hello;

import java.util.Map;

import speak.hi.CustomMap;
import speak.hi.Hi;

public class Hello {

    private Hi hi;

    Hello(Hi hi) {
        this.hi = hi;
    }

    public String sayHello() {
        return "Hello";
    }

    public String sayHi() {
        return hi.sayHi();
    }

    public Map<String, Object> getMap() {
        return hi.getMap();
    }

    public void clearMap() {
        hi.getMap().clear();
    }

    public void discardMap() {
        CustomMap map = (CustomMap) hi.getMap();
        map.discard();
    }

    public static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
        System.out.println(hello.getMap());
        hello.clearMap();
        System.out.println("--");
        hello.discardMap();
    }

}
Run Code Online (Sandbox Code Playgroud)

Hi.java

package speak.hi;

import java.util.HashMap;
import java.util.Map;

public class Hi {
    public String sayHi() {
        return "Hi";
    }

    public Map<String, Object> getMap() {
        return new CustomMap<String, Object>();
    }
}
Run Code Online (Sandbox Code Playgroud)

CustomMap.java

package speak.hi;

import java.util.HashMap;

public class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,直到我public从中删除访问说明符CustomMap

package speak.hi;

import java.util.HashMap;

class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器大喊大叫

类型speak.hi.CustomMap不可见

现在,如果我没有选项来修改speak.hi.CustomMap(第三方JAR等)有什么办法我仍然可以使用CustomMapspeak.hello.Hello


我知道的一个选项是移动speak.hello.Hellospeak.hi.HelloNow Hello在包中speak.hi它可以访问包私有类Hi


有没有其他方法可以做到这一点?或许使用反思?


编辑:根据@StephenC的要求更新了其他详细信息

Ste*_*n C 10

有没有其他方法可以做到这一点?或许使用反思?

是.如果您的应用程序具有完全权限,则可以使用反射来绕过Java访问规则.

例如,private要从不同的类访问对象的字段,您需要:

  • 获取对象的Class对象.
  • 使用该Class.getDeclaredField(...)方法获取Field字段的对象.
  • 致电Field.setAccessible(true)关闭访问检查.
  • 调用Class.getField(object, Field)以获取字段的值(如果是基本类型,则为盒装值).

如果类本身不可访问,则需要确保不要在源代码中引用类标识符...'cos将导致编译错误.相反,将其引用分配给(例如)类型Object或其他可见超类型的变量,并反射性地对该实例执行更具体的操作.


正如您可能想象的那样,这很乏味且容易出错.建议你找一个更好的方法,比如:

  • 让类的供应商修复导致您需要打破可见性限制的任何东西,
  • 让班级供应商改变他们的知名度,
  • 找到另一种方法来使用不需要打开抽象的类,或者
  • 抛弃它们并找到(或写出)更好的东西.

(一般来说,如果你必须打破一个抽象,那么抽象本身或你使用它的方式都有问题.)


最后,我应该补充一点,不应信任的代码(应该)在安全沙箱中运行,该沙箱阻止了密钥反射操作的使用.


Pra*_*ate 6

以下default方法使用反射调用范围类方法

public void discardMap() {
    //CustomMap map = (CustomMap) hi.getMap();
    //map.discard();
    try {
        Object o =hi.getClass().getMethod("getMap").invoke(hi);
        Method m = o.getClass().getMethod("discard");
        m.setAccessible(true);
        m.invoke(o);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是危险的建议.修复类中的访问修饰符. (4认同)