11

如何在 Java(如 C++)中实现友元概念?

4

4 回答 4

34

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,则例外。

于 2013-01-09T00:02:48.440 回答
6

假设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 BA就可以假装是Bto A2

检查是在运行时而不是编译时完成的,这并不完美。不过没什么大不了的,因为任何第 3 方都只能A.foo()用 a调用null,所以我们想在编译时检查它不可能是一个无辜的错误;它可能是恶意的,所以我们不在乎在编译时警告调用者。

于 2013-01-09T02:47:55.200 回答
1

在 Java 中,您可以将两个(或更多)类放入同一个包中。protected该包中的所有类都可以直接访问带有限定符的所有方法和字段。

于 2013-01-09T00:09:40.033 回答
-2

我想出了另一种实现相同目标的方法。基本上,您检查调用类名称的完全限定名称。如果它与您的“朋友”功能匹配,则您授予访问权限,否则返回 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);
    }
}
于 2013-01-09T01:59:54.717 回答