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


当前回答

我认为c++中的友类就像Java中的内部类概念。使用内部类 实际上可以定义一个外围类和一个封闭类。封闭类具有对其封闭类的public和private成员的完全访问权。 请看下面的链接: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

其他回答

没有使用关键字左右。

你可以使用反射等“作弊”,但我不建议“作弊”。

我更喜欢委托、组合或工厂类(取决于导致这个问题的问题),以避免将其设置为公共类。

如果是“不同包中的接口/实现类”问题,那么我将使用一个公共工厂类,它将与impl包位于同一个包中,并防止impl类的暴露。

如果这是一个“我讨厌让这个类/方法公开,只是为了在不同的包中为其他类提供这个功能”的问题,那么我会在同一个包中使用一个公共委托类,并且只公开“外部”类所需的部分功能。

其中一些决策是由目标服务器类加载体系结构(OSGi包、WAR/EAR等)、部署和包命名约定驱动的。例如,上面提出的解决方案“朋友访问器”模式对于普通的java应用程序非常聪明。我想知道在OSGi中实现它是否会因为类加载风格的不同而变得棘手。

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

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

只考虑

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

我认为,使用朋友访问器模式的方法太复杂了。我不得不面对同样的问题,我使用Java中来自c++的优秀的旧复制构造函数来解决:

public class ProtectedContainer {
    protected String iwantAccess;

    protected ProtectedContainer() {
        super();
        iwantAccess = "Default string";
    }

    protected ProtectedContainer(ProtectedContainer other) {
        super();
        this.iwantAccess = other.iwantAccess;
    }

    public int calcSquare(int x) {
        iwantAccess = "calculated square";
        return x * x;
    }
}

在你的应用程序中,你可以写以下代码:

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

这种方法的优点是只有您的应用程序可以访问受保护的数据。它并不完全是friend关键字的替代。但我认为当您编写自定义库并且需要访问受保护的数据时,它非常适合。

当你必须处理ProtectedContainer的实例时,你可以将ProtectedAccessor包裹在它周围,这样你就获得了访问权。

它还适用于受保护的方法。在API中定义它们。稍后在应用程序中编写一个私有包装器类,并将受保护的方法公开为公共方法。就是这样。

下面是一个具有可重用Friend类的清晰用例示例。这种机制的好处是使用简单。也许给单元测试类比应用程序的其他部分更多的访问权是好的。

首先,这里有一个如何使用Friend类的示例。

public class Owner {
    private final String member = "value";

    public String getMember(final Friend friend) {
        // Make sure only a friend is accepted.
        friend.is(Other.class);
        return member;
    }
}

然后在另一个包中你可以这样做:

public class Other {
    private final Friend friend = new Friend(this);

    public void test() {
        String s = new Owner().getMember(friend);
        System.out.println(s);
    }
}

Friend类如下所示。

public final class Friend {
    private final Class as;

    public Friend(final Object is) {
        as = is.getClass();
    }

    public void is(final Class c) {
        if (c == as)
            return;
        throw new ClassCastException(String.format("%s is not an expected friend.", as.getName()));
    }

    public void is(final Class... classes) {
        for (final Class c : classes)
            if (c == as)
                return;
        is((Class)null);
    }
}

然而,问题是,它可能会被这样滥用:

public class Abuser {
    public void doBadThings() {
        Friend badFriend = new Friend(new Other());
        String s = new Owner().getMember(badFriend);
        System.out.println(s);
    }
}

现在,Other类可能确实没有任何公共构造函数,因此不可能实现上面的Abuser代码。但是,如果您的类确实有一个公共构造函数,那么可能建议将Friend类复制为内部类。以Other2类为例:

public class Other2 {
    private final Friend friend = new Friend();

    public final class Friend {
        private Friend() {}
        public void check() {}
    }

    public void test() {
        String s = new Owner2().getMember(friend);
        System.out.println(s);
    }
}

然后Owner2类是这样的:

public class Owner2 {
    private final String member = "value";

    public String getMember(final Other2.Friend friend) {
        friend.check();
        return member;
    }
}

注意Other2。Friend类有一个私有构造函数,因此这是一种更安全的方式。