我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?
当前回答
在Java中,有可能具有“包相关的友好性”。 这对于单元测试很有用。 如果你在一个方法前没有指定private/public/protected,它将是“friend in the package”。 同一个包中的类将能够访问它,但它在类外部是私有的。
这个规则并不总是为人所知,它很好地近似于c++的“朋友”关键字。 我觉得这是一个很好的替代品。
其他回答
这里有一个小技巧,我用JAVA复制c++的朋友机制。
假设我有一个类罗密欧和另一个类朱丽叶。因为仇恨的原因,他们在不同的包裹(家庭)里。
罗密欧想要拥抱朱丽叶,而朱丽叶只想让罗密欧拥抱她。
在c++中,朱丽叶会宣布罗密欧是(恋人)朋友,但在java中没有这样的事情。
下面是这些类和诀窍:
女士优先:
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
这就是朱丽叶的方法。拥抱是公开的,但你需要一个罗密欧。我喜欢这么称呼它。它用的是罗密欧。Love作为“签名安全”,以确保只有Romeo可以调用此方法并检查爱是否真实,以便运行时在它为空时抛出NullPointerException。
现在,孩子们:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
罗密欧班。爱是公开的,但它的创造者却是私人的。因此,任何人都可以看到它,但只有罗密欧才能建造它。我用了一个静态的参考,所以罗密欧。从未使用过的爱只会被构建一次,不会影响优化。
因此,罗密欧可以拥抱朱丽叶,而且只有他可以,因为只有他可以构建和接近一个罗密欧。Love实例,Juliet需要它来拥抱她(否则她会用NullPointerException打你)。
提供的解决方案可能不是最简单的。另一种方法基于与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;”稍微长一点,但是可以在使用注释保留编译时检查的同时缩短它。
没有使用关键字左右。
你可以使用反射等“作弊”,但我不建议“作弊”。
从Java 9开始,可以使用模块在许多情况下解决这个问题。
我认为c++中的友类就像Java中的内部类概念。使用内部类 实际上可以定义一个外围类和一个封闭类。封闭类具有对其封闭类的public和private成员的完全访问权。 请看下面的链接: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html