如何在 Java(如 C++)中实现友元概念?
4 回答
Java 没有 C++ 中的朋友关键字。但是,有一种方法可以模仿它;一种实际上可以提供更精确控制的方法。假设您有类 A 和 B。B 需要访问 A 中的某些私有方法或字段。
public class A {
private int privateInt = 31415;
public class SomePrivateMethods {
public int getSomethingPrivate() { return privateInt; }
private SomePrivateMethods() { } // no public constructor
}
public void giveKeyTo(B other) {
other.receiveKey(new SomePrivateMethods());
}
}
public class B {
private A.SomePrivateMethods key;
public void receiveKey(A.SomePrivateMethods key) {
this.key = key;
}
public void usageExample() {
A anA = new A();
// int foo = anA.privateInt; // doesn't work, not accessible
anA.giveKeyTo(this);
int fii = key.getSomethingPrivate();
System.out.println(fii);
}
}
usageExample() 显示了它是如何工作的。B 的实例无权访问 A 实例的私有字段或方法。但是通过调用 giveKeyTo(),B 类可以访问。没有其他类可以访问该方法,因为它 a 需要一个有效的 B 作为参数。构造函数是私有的。
然后类 B 可以使用密钥中传递给它的任何方法。这虽然比 C++ 的朋友关键字设置起来更笨拙,但更细粒度。A 类可以准确地选择将哪些方法公开给哪些类。
现在,在上述情况下,A 正在授予对 B 的所有实例和 B 的子类实例的访问权限。如果不需要后者,则 giveKeyTo() 方法可以在内部使用 getClass() 检查 other 的确切类型,然后抛出如果不完全是 B,则例外。
假设A.foo()
只应由 调用B
。这可以通过只能由 生成的令牌来安排B
。
public class B
{
public static class ToA { private ToA(){} }
private static final ToA b2a = new ToA();
void test()
{
new A().foo(b2a);
}
}
public class A
{
public void foo(B.ToA b2a)
{
if(b2a==null)
throw new Error("you ain't B");
// ...
}
}
只能B
生成非空B.ToA
令牌。如果两者都A
并且B
不将此令牌泄漏给第 3 方,则没有其他人可以调用A.foo()
如果也A2
想加好友B
,则需要不同的令牌类型。如果是同一个token类型,既然A
得到了from类型的token B
,A
就可以假装是B
to A2
。
检查是在运行时而不是编译时完成的,这并不完美。不过没什么大不了的,因为任何第 3 方都只能A.foo()
用 a调用null
,所以我们想在编译时检查它不可能是一个无辜的错误;它可能是恶意的,所以我们不在乎在编译时警告调用者。
在 Java 中,您可以将两个(或更多)类放入同一个包中。protected
该包中的所有类都可以直接访问带有限定符的所有方法和字段。
我想出了另一种实现相同目标的方法。基本上,您检查调用类名称的完全限定名称。如果它与您的“朋友”功能匹配,则您授予访问权限,否则返回 null。
public class A {
private static int privateInt = 31415;
public static int getPrivateInt() {
if(Throwable().getStackTrace()[1].getClassName().equals(new String("example.java.testing.B")))
{
return privateInt;
}
else
{
return null;
}
}
}
package example.java.testing;
public class B {
public void usageExample() {
int foo = A.getPrivateInt; // works only for B
System.out.println(foo);
}
}