【问题标题】:Implementation of Friend concept in Java [duplicate]Java中Friend概念的实现[重复]
【发布时间】:2012-12-22 23:16:11
【问题描述】:

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

【问题讨论】:

  • 即使我使用 C++ 编写代码,我也非常谨慎地使用friend,如果有的话。提供一个可供所有人使用的公共接口要安全得多。
  • 我没有在我的代码中使用 Java 中的朋友概念。我只是为了好奇而好奇。
  • @Code-Guru 公共界面有什么帮助?我可能在这里遗漏了一些东西。您可以发布一些链接以提供更多详细信息吗?
  • 描述您实际解决的问题可能会很有用,您可能会在 C++ 中使用朋友来解决这个问题。
  • @AlastorMoody 如果您需要这种控制,您的回答肯定可以解决问题。正如我在上面的评论中所说,我通常什至不提供 setter 方法(即我的类几乎都是不可变的)。只要您注意返回对非不可变类(例如集合)的引用,读取访问权限通常是无害的。

标签: java implementation friend


【解决方案1】:

Java 没有 C++ 中的friend 关键字。但是,有一种方法可以模仿它;一种实际上可以提供更精确控制的方法。假设你有 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 则抛出异常。

【讨论】:

  • 非常好。实现这一点的更好方法可能是将B 类声明为final,避免潜在的安全风险。
  • 我认为这就是:如果你想在A和B之间共享api的最小部分,只需提取C。至少这是你在实践中所做的。那时,我会将字段移到 C 中,因为将其保留在 A 中没有任何优势(至少我看不到它)。
【解决方案2】:

假设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 令牌。如果AB 都没有将此令牌泄露给第 3 方, 没有其他人可以调用A.foo()

如果A2 也想与B 成为朋友,它需要不同的令牌类型。如果是同一个token类型,由于AB得到了一个token类型,A可以伪装成BA2

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 确实,但是如果已经存在遗留代码并且重构会很麻烦并且您不想更改字段和方法的可见性,那么设置这样的朋友功能可能是一种选择。我同意,它的可读性不高,甚至不推荐,但它可以作为一种快速修复。
    • 我觉得有必要指出这是最后一道防线,别无选择,最坏的情况,欢迎到技术债务的次贷水平,当它成为永久临时或临时永久选项时。跨度>
    • 我认识的几乎所有人都会将受影响的方法定义为公开的,但你的方法很有趣
    • @AlexWien 我想伤害所有这些人......
    • @TonyHopkinson 它还取决于它是否是某些人使用的公共 API,或者该代码是否仅由两个人在程序的剩余生命周期中使用。
    【解决方案4】:

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

    【讨论】:

    • 我不知道,为什么要这样做投票。对我来说,这是一种有效的方式。但我会抛出一个足够的异常而不是返回 null。另外我会使用'Thread.currentThread().getStackTrace()'而不是'Throwable().getStackTrace()'。此外,我不会写'new String("example.java.testing.B")',而是写'example.java.testing.B.class.getName()'。 (可以通过参考搜索找到)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多