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

时间:2011-11-25 10:48:03

标签: 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();
    }

}

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>();
    }
}

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");
    }
}

这可以正常工作,直到我从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");
    }
}

编译器大喊

  

类型speak.hi.CustomMap不是可见

现在如果我没有选项来修改speak.hi.CustomMap(第三方jar等等)。有什么办法可以使用CustomMap中的speak.hello.Hello吗?


我知道的一个选项是将speak.hello.Hello移至speak.hi.Hello,因为现在Hello位于包speak.hi中,它可以访问包私有类Hi


还有其他办法吗?或许使用反射?


编辑:根据@StephenC

的要求更新了其他详细信息

10 个答案:

答案 0 :(得分:10)

  

还有其他办法吗?或许使用反射?

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

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

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

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


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

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

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


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

答案 1 :(得分: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();
    }
}

答案 2 :(得分:1)

不可能。安全模型是这样的:一个提供安全性的模型:)如果您设计了Hi类并将其交付给具有私人访问权限的客户,您会不希望他们能够绕过您的限制,是吗?

答案 3 :(得分:1)

我不建议使用非API类,因为它们可能会在以后的任何版本中更改,并且可能会破坏您的代码。

你是怎么知道这堂课的?它是一个开源库吗?

尝试联系图书馆的作者,告诉他们您的用例并找到与他们一起提供公共API的方法。如果它是一个开源库,你可以通过提供补丁来帮助他们。

答案 4 :(得分:1)

这可能是不可能的,因为:

班级:

可以从同一个包中访问类吗?

  • public:yes
  • protected:yes
  • 默认:是
  • 私人:没有

可以从不同的包中访问类吗?

  • public:yes
  • protected:no
  • 默认值:除非它是子类
  • 私人:没有

答案 5 :(得分:1)

为了完整起见,添加此解决方案。

我知道的一个选项是将speak.hello.Hello移动到speak.hi.Hello,因为现在Hello是在package speak.hi它可以访问包私有类嗨

package speak.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 static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
    }

}

答案 6 :(得分:0)

我认为如果某个库的作者没有将特定类作为公共API的一部分,那是因为他们不希望其他人使用它。你应该尊重这个决定,即使你可以使用反射打破它。使用私有API只是糟糕的编程。

答案 7 :(得分:0)

第一个类在包a.b.c.class1中,但class1是私有的以及抽象的 第二类在包中a.b.c.class2扩展class1但class2是public

第3课在x.y.z.cl​​ass3包中

为了访问第3类中的class1,您可以编写如下内容: -

类baseClass =(new class2())。getClass(); 并使用其超类的实例然后使用: -  baseClass.getSuperClass(); 并在任何你想要的地方使用它。

但是,基础类再次被抽象和私有,原因是因此不建议这样做,但是再次将此解决方案用作解决方法。

答案 8 :(得分:-1)

不可能您无法将Hi类创建为私有。

答案 9 :(得分:-1)

我认为默认情况下,课程将“默认”(包私有,你可以说),而不是“私人”。因此可以在同一个包中访问它。

此外,你不能在Java中制作任何*顶级类私有。

如果你想创建一个类默认并且仍然能够在其他包中访问它那么具有访问说明符(修饰符)的目的是什么?

您需要将类设为公开或移至同一个包。