我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?


当前回答

Java的设计者明确地拒绝了朋友的概念,因为它在c++中是有效的。你把你的"朋友"放在同一个包裹里。私有的、受保护的和打包的安全性是作为语言设计的一部分强制执行的。

James Gosling希望Java成为没有错误的c++。我相信他觉得这个朋友是一个错误,因为它违反了面向对象的原则。包提供了一种合理的方式来组织组件,而不会对OOP过于纯粹。

NR指出,你可以使用反射来作弊,但即使这样也只在你没有使用SecurityManager的情况下有效。如果您启用了Java标准安全性,那么您将无法通过反射进行欺骗,除非您编写了专门允许反射的安全策略。

其他回答

如果你想访问受保护的方法,你可以创建一个你想要使用的类的子类,它将你想要使用的方法公开为公共的(或者是更安全的命名空间内部的),并在你的类中有一个类的实例(将它用作代理)。

就私人方法而言(我认为)你运气不好。

据我所知,这是不可能的。

也许,你可以给我们更多关于你的设计的细节。像这样的问题很可能是设计缺陷造成的。

只考虑

如果这些类如此密切相关,为什么它们在不同的包中? A是否访问B的私有成员,或者操作是否应该移动到类B并由A触发? 这是真正的调用还是事件处理更好?

Java的设计者明确地拒绝了朋友的概念,因为它在c++中是有效的。你把你的"朋友"放在同一个包裹里。私有的、受保护的和打包的安全性是作为语言设计的一部分强制执行的。

James Gosling希望Java成为没有错误的c++。我相信他觉得这个朋友是一个错误,因为它违反了面向对象的原则。包提供了一种合理的方式来组织组件,而不会对OOP过于纯粹。

NR指出,你可以使用反射来作弊,但即使这样也只在你没有使用SecurityManager的情况下有效。如果您启用了Java标准安全性,那么您将无法通过反射进行欺骗,除非您编写了专门允许反射的安全策略。

Eirikma的回答简单而出色。我可能还要补充一点:与其使用一个公共可访问的方法getFriend()来获取一个不能使用的朋友,不如更进一步,不允许获取没有令牌的朋友:getFriend(Service.FriendToken)。这个FriendToken将是一个具有私有构造函数的内部公共类,因此只有Service可以实例化一个。

我发现解决这个问题的一个方法是创建一个accessor对象,如下所示:

class Foo {
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* This is the accessor. Anyone with a reference to this has special access. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    /** You get an accessor by calling this method. This method can only
     * be called once, so calling is like claiming ownership of the accessor. */
    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }
}

调用getAccessor()的第一个代码声明访问器的所有权。通常,这是创建对象的代码。

Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.

这也比c++的友元机制有一个优势,因为它允许您在每个实例级别限制访问,而不是在每个类级别限制访问。通过控制访问器引用,可以控制对对象的访问。你也可以创建多个访问器,并给每个访问器不同的访问权限,这允许细粒度地控制哪些代码可以访问哪些:

class Foo {
    private String secret;
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* Normal accessor. Can write to locked, but not read secret. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }

    /* Super accessor. Allows access to secret. */
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    private FooSuperAccessor superAccessor;

    public FooSuperAccessor getAccessor() {
        if (superAccessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return superAccessor = new FooSuperAccessor();
    }
}

最后,如果您想让事情更有条理,您可以创建一个引用对象,它将所有内容放在一起。这允许您使用一个方法调用来声明所有访问器,并将它们与其链接的实例保持在一起。一旦你有了引用,你就可以把访问器传递给需要它的代码:

class Foo {
    private String secret;
    private String locked;

    public String getLocked() { return locked; }

    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    public class FooReference {
        public final Foo foo;
        public final FooAccessor accessor;
        public final FooSuperAccessor superAccessor;

        private FooReference() {
            this.foo = Foo.this;
            this.accessor = new FooAccessor();
            this.superAccessor = new FooSuperAccessor();
        }
    }

    private FooReference reference;

    /* Beware, anyone with this object has *all* the accessors! */
    public FooReference getReference() {
        if (reference != null)
            throw new IllegalStateException("Cannot return reference more than once!");
        return reference = new FooReference();
    }
}

在多次碰头会之后(不是那种好的碰头会),这是我的最终解决方案,我非常喜欢它。它灵活,使用简单,并且允许对类访问进行很好的控制。(只有参考的访问非常有用。)如果你使用protected而不是private作为访问器/引用,Foo的子类甚至可以从getReference返回扩展的引用。它也不需要任何反射,因此可以在任何环境中使用。