我希望能够在一个包中编写一个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返回扩展的引用。它也不需要任何反射,因此可以在任何环境中使用。
其他回答
如果你想访问受保护的方法,你可以创建一个你想要使用的类的子类,它将你想要使用的方法公开为公共的(或者是更安全的命名空间内部的),并在你的类中有一个类的实例(将它用作代理)。
就私人方法而言(我认为)你运气不好。
这里有一个小技巧,我用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打你)。
据我所知,这是不可能的。
也许,你可以给我们更多关于你的设计的细节。像这样的问题很可能是设计缺陷造成的。
只考虑
如果这些类如此密切相关,为什么它们在不同的包中? A是否访问B的私有成员,或者操作是否应该移动到类B并由A触发? 这是真正的调用还是事件处理更好?
没有使用关键字左右。
你可以使用反射等“作弊”,但我不建议“作弊”。
Eirikma的回答简单而出色。我可能还要补充一点:与其使用一个公共可访问的方法getFriend()来获取一个不能使用的朋友,不如更进一步,不允许获取没有令牌的朋友:getFriend(Service.FriendToken)。这个FriendToken将是一个具有私有构造函数的内部公共类,因此只有Service可以实例化一个。
推荐文章
- 如何找到Java堆大小和内存使用(Linux)?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c#中朋友的对等物是什么?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- c++枚举类可以有方法吗?
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?