题
在程序上我写作我有一个类 RestrictedUser
和类 User
这是来自 RestrictedUser.
我试图隐藏的用户的具体方法的铸造 RestrictedUser
但是当我做的铸造用户的方法仍然可用。此外,当我运行调试器类型的变量来作为 User
.
RestrictedUser restricted = regularUser;
不了铸造Java隐藏的子类方法和领域或者我做错了什么?是否有一个解决方法?
感谢
解决方案
如果你要尝试运行这段代码:
User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--
你会得到一个编译错误。这不会以任何方式,形状或形式的安全,因为即使你要绕过RestrictedUser
,比方说,另一种方法,该方法可以做到这一点:
if (restricted instanceof User) {
User realUser = (User)restricted;
realUser.methodDefinedInTheUserClass(); // !!
}
和您的“限制”用户不被如此限制了。
该目的上显示的是调试器作为User
对象,因为它是作为User
对象,即使对象引用存储在一个RestrictedUser
变量。基本上,把一个子类的实例与父类的类型的变量会确实是“隐藏”的子类的方法和字段,但不牢固。
其他提示
我不知道你问什么,你所使用的术语是有点不清楚,但在这里不用。如果你有一个超类和子类你可以使他们的私人藏在从子类的父类的方法。如果您需要在超公共方法是无形的,你的运气了。如果你投子类的父类,然后在子类中的公共方法不再可见。
一个建议可以帮助你将是使用组合物,而不是继承,提取敏感的方法成一个单独的类,然后根据需要插入那个类的一个适当的实例到对象。如果你需要,你可以委托从类的注射类的方法。
你混淆静态类型和动态类型。
静态类型是参考的类型。当您铸造从派生基地,你告诉说,就你和它知道,事情指出编译器是一个基础。也就是说,你允诺指向的对象为null,或基地或基地派生出来的。这就是说,它有基本的公共接口。
您将无法再能调用派生声明的方法(而不是库),但是当你调用由派生,你会得到Derived的重写版本覆盖的任何基础的方法。
如果你需要保护的子类,你可以使用委托模式:
public class Protect extends Superclass { // better implement an interface here
private final Subclass subclass;
public Protect(Subclass subclass) {
this.subclass = subclass;
}
public void openMethod1(args) {
this.subclass.openMethod1(args);
}
public int openMethod2(args) {
return this.subclass.openMethod2(args);
}
...
}
您也可以考虑使用的java .lang.reflect.Proxy 结果 []]
<强>爪哇强>
在Java中,你可以永远“隐藏”从对象的东西。 编译器可以忘记你是在你有特别的实例详细了解。 (就像它是什么子类)时,调试傻到编译器要多得多,所以它会告诉你什么实际类型的当前实例,这是您遇到大概是什么。
<强> OOP 强>
这听起来像你正在编写需要知道什么类型的你正在使用对象的这OOP不建议,但往往实际的原因所需的逻辑。
<强>限制形容词强>
我在这个问题评论说,你应该对什么是基类在这里清楚。直观RestrictedUser应该是用户的一个子类,因为顾名思义更加专业的类型。 (名称与其有一个附加的Active形容词。)在你的情况,这是特殊的,因为这是一个限制的形容词这将让你把用户作为RestrictedUser完全正常的子类。我建议重命名两到类似:BasicUser / UserWithId和NonRestrictedUser避免限制形容词是这里的混乱的部分。
另外,不要被诱惑认为你可以躲在一个匿名类方法中的任何。例如,这将不会提供您所期望的隐私:
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("Hello, world!");
}
public void secretSquirrel() {
System.out.println("Surprise!");
}
}
您可能无法说出真正的类型run
的(为了转换为直接),但你仍然可以得到它的类getClass()
,你可以使用反射调用secretSquirrel
。 (即使secretSquirrel
是私有的,如果你没有SecurityManager
,或者如果它被设置为允许访问性,反射也可以调用私有方法。)
我想你也许已经做了一个可怜的命名的选择:我想RestrictedUser
将User
的一个子类,而不是其他方式,所以我将使用UnrestrictedUser
作为子类
如果您上溯造型的UnrestrictedUser
到RestrictedUser
,您的参考只能访问RestrictedUser
声明的方法。但是,如果你垂头丧气回一个UnrestrictedUser
,你就可以访问所有的方法。由于Java对象知道是什么类型的,他们真的是,任何实际上是一个UnrestrictedUser
总是可以什么类型的引用您正在使用(甚至Object
)转换回吧,不管。有不可避免的和语言的一部分。但是,你可以将其隐藏背后某种代理的,但在你的情况下,可能违背了目的。
此外,在Java中,所有非静态方法是通过默认虚拟。这意味着,如果UnrestrictedUser
覆盖在RestrictedUser
宣布一些方法,那么UnrestrictedUser
版本将被使用,即使你通过RestrictedUser
参考访问它。如果您需要调用父类的版本,你可以通过调用RestrictedUser.variableName.someMethod()
非虚调用。然而,你可能不应该经常(最少的一个原因是,它打破封装)。使用本
我也在想这个问题引出了另一个问题。你打算如何调用这个方法?这似乎是一个有层次结构类的子类,其中引入了新的方法签名要求的instanceof在呼叫者检查。
您可以更新您的问题,包括这样一个场景,你会调用的问题,这些方法呢?也许我们将能够帮助更多的。
在技术答案已经由约翰·Calsbeek给出。我要考虑的设计问题。你所要做的是防止一些代码段,或一些开发商,从寂寂User类东西。你希望他们把所有的用户,如果他们RestrictedUsers。
你会做到这一点的方法是使用权限。您需要防止开发商在用户类有问题的访问。你也许可以做到这一点通过将其放置在一个包,并限制访问的包。