假设我有包裹:
com.mycomp.packone
com.mycomp.packtwo
我是否有办法从packtwo中的类访问packone中的类的受保护成员,但是不允许公共用户这样做?我能想到的唯一方法是使用受保护的访问和使用子类。但这只是将问题推入子类,因为我也希望在那里有相同的访问限制。
这方面的背景是我们正在重新设计我们的主要API并希望使其更加模块化。现在它只是一个巨大的包装。我不是为了这个设计,但我认为这是因为有很多受保护的用法。
答案 0 :(得分:1)
Java包受到严格限制(恕我直言),因为对它们之间的包层次结构和包含没有特殊处理。每个包都是独立的,点符号仅供人眼使用。限制对特定客户的访问的能力也被打破了(它有时让我希望C ++的友谊机制......)
AFAIK,这是你现在唯一的选择,直到Java 7有希望解决问题。
你可以考虑的一件事(如果你的项目适合)是使用像OGSi这样的东西。它的模块化和导出基础结构使您可以执行比语言允许的更精细的操作。
答案 1 :(得分:0)
protected
修饰符是您现在唯一的选择(JDK7还没有出来)。正如您所说,这仍然允许其他包中的子类访问受保护的成员。您可以通过将类声明为final
来阻止子类化,但我不确定这是否与您的用例兼容。同样重要的是要记住,Java中的 all 访问修饰符仅仅是建议,并且很容易被反射绕过。
答案 2 :(得分:0)
像Asaph所说的那样,使用反射破坏规则(使用getDeclaredMethod代替字段):
package com.mycomp.packone;
public class Introvert {
protected String secret = "TOP SECRET!";
}
package com.mycomp.packtwo;
import java.lang.reflect.Field;
public class Extrovert {
public String talk(){
return this.getSecret();
}
@SuppressWarnings("unchecked")
protected String getSecret(){ // everybody in packtwo can call me!
try {
Class introvertClass = Class.forName("com.mycomp.packone.Introvert");
Object introvert = introvertClass.newInstance();
Field secretField = introvertClass.getDeclaredField("secret");
secretField.setAccessible(true);
return (String) secretField.get(introvert);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args){
Extrovert extrovert = new Extrovert();
System.out.println(extrovert.talk());
}
}
答案 3 :(得分:0)
考虑重新设计您的API,使其具有“外部”和“内部”API。
虽然您无法使用本机Java访问修饰符执行此操作,但您可以使用以下命令打包您的类:
package com.mycomp
package com.mycomp.internal
在 com.mycomp 包中,您发布允许“公共”用户依赖的公共API;大多数情况下,此包中的接口多于类。
com.mycomp.internal 包是实现 com.mycomp 中的大多数接口的地方。命名内部包有效地告诉人们包中的类是API的内部类,并且选择依赖内部包可能会在将来的版本中破坏它们的代码。更重要的是,如果您可以使用OSGi,则只能导出 com.mycomp 包,从而使 com.mycomp.internal 有效地“隐藏”在世界其他地方
FWIW,mockito为其API采用这种打包方法。
这种方法的缺点是依赖惯例。