我希望能够在一个包中编写一个Java类,它可以访问另一个包中类的非公共方法,而不必使它成为另一个类的子类。这可能吗?
当前回答
没有使用关键字左右。
你可以使用反射等“作弊”,但我不建议“作弊”。
其他回答
Java的设计者明确地拒绝了朋友的概念,因为它在c++中是有效的。你把你的"朋友"放在同一个包裹里。私有的、受保护的和打包的安全性是作为语言设计的一部分强制执行的。
James Gosling希望Java成为没有错误的c++。我相信他觉得这个朋友是一个错误,因为它违反了面向对象的原则。包提供了一种合理的方式来组织组件,而不会对OOP过于纯粹。
NR指出,你可以使用反射来作弊,但即使这样也只在你没有使用SecurityManager的情况下有效。如果您启用了Java标准安全性,那么您将无法通过反射进行欺骗,除非您编写了专门允许反射的安全策略。
我发现解决这个问题的一个方法是创建一个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返回扩展的引用。它也不需要任何反射,因此可以在任何环境中使用。
我更喜欢委托、组合或工厂类(取决于导致这个问题的问题),以避免将其设置为公共类。
如果是“不同包中的接口/实现类”问题,那么我将使用一个公共工厂类,它将与impl包位于同一个包中,并防止impl类的暴露。
如果这是一个“我讨厌让这个类/方法公开,只是为了在不同的包中为其他类提供这个功能”的问题,那么我会在同一个包中使用一个公共委托类,并且只公开“外部”类所需的部分功能。
其中一些决策是由目标服务器类加载体系结构(OSGi包、WAR/EAR等)、部署和包命名约定驱动的。例如,上面提出的解决方案“朋友访问器”模式对于普通的java应用程序非常聪明。我想知道在OSGi中实现它是否会因为类加载风格的不同而变得棘手。
如果你想访问受保护的方法,你可以创建一个你想要使用的类的子类,它将你想要使用的方法公开为公共的(或者是更安全的命名空间内部的),并在你的类中有一个类的实例(将它用作代理)。
就私人方法而言(我认为)你运气不好。
我认为,使用朋友访问器模式的方法太复杂了。我不得不面对同样的问题,我使用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中定义它们。稍后在应用程序中编写一个私有包装器类,并将受保护的方法公开为公共方法。就是这样。
推荐文章
- 如何找到Java堆大小和内存使用(Linux)?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c#中朋友的对等物是什么?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- c++枚举类可以有方法吗?
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?