我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?
当前回答
我认为c++中的友类就像Java中的内部类概念。使用内部类 实际上可以定义一个外围类和一个封闭类。封闭类具有对其封闭类的public和private成员的完全访问权。 请看下面的链接: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
其他回答
对于您的问题,有两种解决方案不涉及将所有类保存在同一个包中。
第一种是使用(实用API设计,Tulach 2008)中描述的朋友访问器/朋友包模式。
第二种是使用OSGi。这里有一篇文章解释了OSGi是如何实现这一点的。
相关问题:1、2和3。
我曾经见过一个基于反射的解决方案,它在运行时使用反射进行“好友检查”,并检查调用堆栈,以查看调用该方法的类是否允许这样做。作为运行时检查,它有一个明显的缺点。
这里有一个小技巧,我用JAVA复制c++的朋友机制。
假设我有一个类罗密欧和另一个类朱丽叶。因为仇恨的原因,他们在不同的包裹(家庭)里。
罗密欧想要拥抱朱丽叶,而朱丽叶只想让罗密欧拥抱她。
在c++中,朱丽叶会宣布罗密欧是(恋人)朋友,但在java中没有这样的事情。
下面是这些类和诀窍:
女士优先:
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
这就是朱丽叶的方法。拥抱是公开的,但你需要一个罗密欧。我喜欢这么称呼它。它用的是罗密欧。Love作为“签名安全”,以确保只有Romeo可以调用此方法并检查爱是否真实,以便运行时在它为空时抛出NullPointerException。
现在,孩子们:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
罗密欧班。爱是公开的,但它的创造者却是私人的。因此,任何人都可以看到它,但只有罗密欧才能建造它。我用了一个静态的参考,所以罗密欧。从未使用过的爱只会被构建一次,不会影响优化。
因此,罗密欧可以拥抱朱丽叶,而且只有他可以,因为只有他可以构建和接近一个罗密欧。Love实例,Juliet需要它来拥抱她(否则她会用NullPointerException打你)。
Java的设计者明确地拒绝了朋友的概念,因为它在c++中是有效的。你把你的"朋友"放在同一个包裹里。私有的、受保护的和打包的安全性是作为语言设计的一部分强制执行的。
James Gosling希望Java成为没有错误的c++。我相信他觉得这个朋友是一个错误,因为它违反了面向对象的原则。包提供了一种合理的方式来组织组件,而不会对OOP过于纯粹。
NR指出,你可以使用反射来作弊,但即使这样也只在你没有使用SecurityManager的情况下有效。如果您启用了Java标准安全性,那么您将无法通过反射进行欺骗,除非您编写了专门允许反射的安全策略。
我认为,使用朋友访问器模式的方法太复杂了。我不得不面对同样的问题,我使用Java中来自c++的优秀的旧复制构造函数来解决:
public class ProtectedContainer {
protected String iwantAccess;
protected ProtectedContainer() {
super();
iwantAccess = "Default string";
}
protected ProtectedContainer(ProtectedContainer other) {
super();
this.iwantAccess = other.iwantAccess;
}
public int calcSquare(int x) {
iwantAccess = "calculated square";
return x * x;
}
}
在你的应用程序中,你可以写以下代码:
public class MyApp {
private static class ProtectedAccessor extends ProtectedContainer {
protected ProtectedAccessor() {
super();
}
protected PrivateAccessor(ProtectedContainer prot) {
super(prot);
}
public String exposeProtected() {
return iwantAccess;
}
}
}
这种方法的优点是只有您的应用程序可以访问受保护的数据。它并不完全是friend关键字的替代。但我认为当您编写自定义库并且需要访问受保护的数据时,它非常适合。
当你必须处理ProtectedContainer的实例时,你可以将ProtectedAccessor包裹在它周围,这样你就获得了访问权。
它还适用于受保护的方法。在API中定义它们。稍后在应用程序中编写一个私有包装器类,并将受保护的方法公开为公共方法。就是这样。