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

我有以下课程

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 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 getMap() { return new CustomMap(); } } 

CustomMap.java

 package speak.hi; import java.util.HashMap; public class CustomMap extends HashMap { 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"); } } 

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

 package speak.hi; import java.util.HashMap; class CustomMap extends HashMap { 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等)。有什么方法我还可以使用speak.hello.Hello CustomMap


我知道的一个选择是将speak.hello.Hello移动到speak.hi.Hello因为Now Hello在包中speak.hi它可以访问包私有类Hi


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


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

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

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

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

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

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


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

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

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


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

以下方法使用reflection调用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(); } } 

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

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

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

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

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

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

我知道的一个选择是将speak.hello.Hello移动到speak.hi.Hello,因为Now Hello在包中发言。它可以访问包私有类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()); } } 

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

分类:

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

  • 公众:是的
  • 保护:是的
  • 默认:是的
  • 私人:不

从不同的包可以访问课程?

  • 公众:是的
  • 受保护的:没有
  • 默认值:除非它是子类
  • 私人:不

第一个类是在包abcclass1中,但是class1是私有的,而抽象的第二个类在包中,abcclass2扩展了class1但是class2是public

第三类是包xyzclass3

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

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

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

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

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

此外,您不能在Java中使任何*顶级类私有。

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

您需要将类设置为public或移动到同一个包。