我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?
当前回答
Eirikma的回答简单而出色。我可能还要补充一点:与其使用一个公共可访问的方法getFriend()来获取一个不能使用的朋友,不如更进一步,不允许获取没有令牌的朋友:getFriend(Service.FriendToken)。这个FriendToken将是一个具有私有构造函数的内部公共类,因此只有Service可以实例化一个。
其他回答
提供的解决方案可能不是最简单的。另一种方法基于与c++相同的思想:除了所有者与自己为友的特定类外,在包/私有作用域之外不能访问私有成员。
需要对成员进行友元访问的类应该创建一个内部公共抽象“友元类”,拥有隐藏属性的类可以通过返回实现访问实现方法的子类导出对该类的访问。友元类的“API”方法可以是私有的,因此在需要友元访问的类外部无法访问它。它的唯一语句是对导出类实现的抽象受保护成员的调用。
代码如下:
首先是验证它是否实际工作的测试:
package application;
import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;
public class EntityFriendTest extends TestCase {
public void testFriendsAreOkay() {
Entity entity = new Entity();
Service service = new Service();
assertNull("entity should not be processed yet", entity.getPublicData());
service.processEntity(entity);
assertNotNull("entity should be processed now", entity.getPublicData());
}
}
然后,需要友方访问Entity的包私有成员的服务:
package application.service;
import application.entity.Entity;
public class Service {
public void processEntity(Entity entity) {
String value = entity.getFriend().getEntityPackagePrivateData();
entity.setPublicData(value);
}
/**
* Class that Entity explicitly can expose private aspects to subclasses of.
* Public, so the class itself is visible in Entity's package.
*/
public static abstract class EntityFriend {
/**
* Access method: private not visible (a.k.a 'friendly') outside enclosing class.
*/
private String getEntityPackagePrivateData() {
return getEntityPackagePrivateDataImpl();
}
/** contribute access to private member by implementing this */
protected abstract String getEntityPackagePrivateDataImpl();
}
}
最后:Entity类,它仅对类application.service.Service提供对包私有成员的友好访问。
package application.entity;
import application.service.Service;
public class Entity {
private String publicData;
private String packagePrivateData = "secret";
public String getPublicData() {
return publicData;
}
public void setPublicData(String publicData) {
this.publicData = publicData;
}
String getPackagePrivateData() {
return packagePrivateData;
}
/** provide access to proteced method for Service'e helper class */
public Service.EntityFriend getFriend() {
return new Service.EntityFriend() {
protected String getEntityPackagePrivateDataImpl() {
return getPackagePrivateData();
}
};
}
}
好吧,我必须承认它比“friend service:: service;”稍微长一点,但是可以在使用注释保留编译时检查的同时缩短它。
我更喜欢委托、组合或工厂类(取决于导致这个问题的问题),以避免将其设置为公共类。
如果是“不同包中的接口/实现类”问题,那么我将使用一个公共工厂类,它将与impl包位于同一个包中,并防止impl类的暴露。
如果这是一个“我讨厌让这个类/方法公开,只是为了在不同的包中为其他类提供这个功能”的问题,那么我会在同一个包中使用一个公共委托类,并且只公开“外部”类所需的部分功能。
其中一些决策是由目标服务器类加载体系结构(OSGi包、WAR/EAR等)、部署和包命名约定驱动的。例如,上面提出的解决方案“朋友访问器”模式对于普通的java应用程序非常聪明。我想知道在OSGi中实现它是否会因为类加载风格的不同而变得棘手。
在Java中,有可能具有“包相关的友好性”。 这对于单元测试很有用。 如果你在一个方法前没有指定private/public/protected,它将是“friend in the package”。 同一个包中的类将能够访问它,但它在类外部是私有的。
这个规则并不总是为人所知,它很好地近似于c++的“朋友”关键字。 我觉得这是一个很好的替代品。
例如,“朋友”概念在Java中很有用,可以将API与其实现分开。实现类通常需要访问API类的内部内容,但这些内容不应该公开给API客户端。这可以使用“朋友访问器”模式来实现,如下所示:
通过API公开的类:
package api;
public final class Exposed {
static {
// Declare classes in the implementation package as 'friends'
Accessor.setInstance(new AccessorImpl());
}
// Only accessible by 'friend' classes.
Exposed() {
}
// Only accessible by 'friend' classes.
void sayHello() {
System.out.println("Hello");
}
static final class AccessorImpl extends Accessor {
protected Exposed createExposed() {
return new Exposed();
}
protected void sayHello(Exposed exposed) {
exposed.sayHello();
}
}
}
提供'friend'功能的类:
package impl;
public abstract class Accessor {
private static Accessor instance;
static Accessor getInstance() {
Accessor a = instance;
if (a != null) {
return a;
}
return createInstance();
}
private static Accessor createInstance() {
try {
Class.forName(Exposed.class.getName(), true,
Exposed.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
return instance;
}
public static void setInstance(Accessor accessor) {
if (instance != null) {
throw new IllegalStateException(
"Accessor instance already set");
}
instance = accessor;
}
protected abstract Exposed createExposed();
protected abstract void sayHello(Exposed exposed);
}
从'friend'实现包中的类的访问示例:
package impl;
public final class FriendlyAccessExample {
public static void main(String[] args) {
Accessor accessor = Accessor.getInstance();
Exposed exposed = accessor.createExposed();
accessor.sayHello(exposed);
}
}
我同意在大多数情况下,friend关键字是不必要的。
包的私有(又名。默认值)在大多数情况下(您有一组严重交织的类)就足够了 对于希望访问内部内容的调试类,我通常将方法设置为私有并通过反射访问它。速度在这里通常不重要 有时,您实现的方法是“黑客”或其他容易更改的方法。我将其设为public,但使用@Deprecated表示不应该依赖于此方法。
最后,如果确实有必要,还有其他答案中提到的朋友访问器模式。
推荐文章
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 什么是“参数依赖查找”(又名ADL,或“Koenig查找”)?
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 公共朋友交换成员函数
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- 如何在Go中使用c++
- Java中的操作符重载