我希望能够在一个包中编写一个Java类,它可以访问另一个包中的类的非公共方法,而不必使它成为另一个类的子类.这可能吗?
这是我在JAVA中用来复制C++好友机制的一个小技巧.
让我们说我有一个班级Romeo
和另一个班级Juliet
.出于仇恨原因,他们在不同的包裹(家庭)中.
Romeo
想要cuddle
Juliet
而且Juliet
只想让Romeo
cuddle
她.
在C++中,Juliet
将声明Romeo
为(情人)friend
但在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?");
}
}
所以方法Juliet.cuddle
是,public
但你需要Romeo.Love
调用它.它使用它Romeo.Love
作为"签名安全性"来确保只能Romeo
调用此方法并简单地调用NullPointerException
它,以便运行时将抛出一个null
if Romeo.Love
.
现在男孩:
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);
}
}
该类private
是公共的,但它的构造函数是Romeo
.因此任何人都可以看到它,但只能Romeo.Love
构建它.我使用静态引用,因此Romeo
从未使用它只构造一次,不会影响优化.
因此,cuddle
可Juliet
Romeo.Love
只有他能,因为只有他可以构建和访问一个Juliet
实例,这是必需的cuddle
,以NullPointerException
她的(否则她会拍你一个Romeo
).
Java的设计者明确拒绝了朋友的想法,因为它在C++中有效.你把你的"朋友"放在同一个包里.私有,受保护和打包的安全性是语言设计的一部分.
James Gosling希望Java能够成为C++而不会出错.我相信他觉得朋友是个错误,因为它违反了OOP原则.包提供了一种合理的方式来组织组件而不会过于纯粹的OOP.
NR指出你可以使用反射作弊,但即使只有你没有使用SecurityManager才有效.如果打开Java标准安全性,除非您编写安全策略以明确允许,否则您将无法使用反射作弊.
"朋友"概念在Java中很有用,例如,将API与其实现分开.实现类通常需要访问API类内部,但这些不应该暴露给API客户端.这可以使用'Friend Accessor'模式实现,详情如下:
通过API公开的类:
package api; public final class Exposed { static { // Declare classes in the implementation package as 'friends' Accessor.setInstance(new AccessorImpl()); } // Only accessible by 'friend' classes. Exposed() { } // Only accessible by 'friend' classes. void sayHello() { System.out.println("Hello"); } static final class AccessorImpl extends Accessor { protected Exposed createExposed() { return new Exposed(); } protected void sayHello(Exposed exposed) { exposed.sayHello(); } } }
提供"朋友"功能的课程:
package impl; public abstract class Accessor { private static Accessor instance; static Accessor getInstance() { Accessor a = instance; if (a != null) { return a; } return createInstance(); } private static Accessor createInstance() { try { Class.forName(Exposed.class.getName(), true, Exposed.class.getClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } return instance; } public static void setInstance(Accessor accessor) { if (instance != null) { throw new IllegalStateException( "Accessor instance already set"); } instance = accessor; } protected abstract Exposed createExposed(); protected abstract void sayHello(Exposed exposed); }
来自'friend'实现包中的类的示例访问:
package impl; public final class FriendlyAccessExample { public static void main(String[] args) { Accessor accessor = Accessor.getInstance(); Exposed exposed = accessor.createExposed(); accessor.sayHello(exposed); } }
您的问题有两种解决方案,不涉及将所有类保留在同一个包中.
第一种是使用(Practical API Design,Tulach 2008)中描述的Friend Accessor/Friend Package模式.
第二是使用OSGi.有文章在这里解释的OSGi是如何实现这一点.
相关问题:1,2,和3.
据我所知,这是不可能的.
也许,你可以给我们一些关于你的设计的更多细节.像这样的问题可能是设计缺陷的结果.
考虑一下
如果这些课程密切相关,为什么这些课程在不同的课程中呢?
有A访问B的私人成员还是应该将操作移到B类并由A触发?
这是真正的呼唤还是事件处理更好?