Java中的向上渲染是否隐藏了子类方法和字段?

时间:2009-04-03 04:17:54

标签: java upcasting

在我写的程序中,我有一个类RestrictedUser和类User,它派生自RestrictedUser.我试图通过强制转换为{{来隐藏用户特定的方法1}}但是当我进行转换时,User方法仍然可用。此外,当我运行调试器时,变量的类型显示为RestrictedUser

User

Java中的up cast是否会隐藏子类方法和字段,或者我做错了什么?有解决方法吗?

由于

10 个答案:

答案 0 :(得分:16)

如果您尝试运行此代码:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

你会收到编译错误。这不会以任何方式,形状或形式安全,因为即使您将RestrictedUser传递给另一种方法,该方法也可以这样做:

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

并且您的“受限制”用户不再受限制。

该对象在调试器中显示为User对象,因为它是User对象,即使对象引用存储在RestrictedUser变量中也是如此。基本上,将子类的实例放在具有父类类型的变量中确实会“隐藏”子类的方法和字段,但不是安全的。

答案 1 :(得分:3)

我不确定你所问的是什么,因为你使用的术语有点不清楚但是这里有。如果你有一个超类和一个子类,你可以通过将它们设为私有来隐藏子类中超类的方法。如果你需要超类上的公共方法是隐形的,那你就不走运了。如果将子类强制转换为超类,则子类上的公共方法将不再可见。

可能有用的一个建议是使用组合而不是继承,将敏感方法提取到单独的类中,然后根据需要将该类的适当实例插入到对象中。如果需要,您可以将类中的方法委托给注入的类。

答案 2 :(得分:3)

您会混淆静态类型和动态类型。

静态类型是引用的类型。当您从Derived转发到Base时,您告诉编译器,只要您知道,指向的东西就是Base。也就是说,你承诺指向的对象是null,或Base,或从Base派生的东西。也就是说,它有Base的公共接口。

您将无法调用在Derived中声明的方法(而不是在Base中),但是当您调用Derived重写的任何Base方法时,您将获得Derived的重写版本。

答案 3 :(得分:3)

如果需要保护子类,可以使用Delegate模式:

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
[]]

答案 4 :(得分:2)

Peter's answerJohn's answer是正确的:如果对象的真实类型(客户端代码可以转换为,调用反射方法等)的话,只是转换静态类型将无效可用。你必须以某种方式掩盖真实的类型(正如彼得的回答所提到的那样)。

实际上有一种模式:如果您有权访问JDK源代码,请查看Executors类中的unconfigurable*方法。

答案 5 :(得分:2)

<强>爪哇

在Java中,你永远不能从对象中“隐藏”某些东西。 编译器可以忘记您对特定实例的详细了解。 (就像它的子类一样)调试器比编译器知道得多,所以它会告诉你当前实例的实际类型,这可能就是你所经历的。

<强> OOP

听起来你正在编写逻辑,需要知道你正在使用哪种类型的对象,这在OOP中是不推荐的,但是出于实际原因通常需要这样做。

限制形容词

正如我在对这个问题的评论中所说,你应该清楚这里的基类是什么。直觉RestrictedUser应该是User的子类,因为名称提示更专业的类型。 (名称上有一个额外的主动形容词。)在你的情况下这是特殊的,因为这是一个限制形容词,它可以让你把User作为RestrictedUser的子类完全没问题。我建议将这两个重命名为:BasicUser / UserWithId和NonRestrictedUser,以避免限制形容词,这是令人困惑的部分。

答案 6 :(得分:1)

另外,不要试图认为你可以隐藏匿名类中的方法。例如,这不会提供您期望的隐私:

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

您可能无法命名run的实际类型(为了直接转换为它),但您仍可以使用getClass()获取其类,并且可以调用{{1使用反射。 (即使secretSquirrel是私有的,如果没有secretSquirrel,或者它已被设置为允许访问,则反射也可以调用私有方法。)

答案 7 :(得分:0)

我认为你可能做出了一个糟糕的命名选择:我认为RestrictedUser将是User的子类,而不是相反,所以我将使用UnrestrictedUser作为子类。

如果您将UnrestrictedUser转发为RestrictedUser,则您的引用只能访问RestrictedUser中声明的方法。但是,如果您将其转发回UnrestrictedUser,您将可以访问所有方法。由于Java对象知道它们究竟是什么类型,因此无论您使用何种类型的引用(即使是UnrestrictedUser),实际上Object的任何内容都可以始终返回到它。这是不可避免的,也是语言的一部分。但是,你可以将它隐藏在某种代理之后,但在你的情况下可能会破坏目的。

此外,在Java中,默认情况下所有非静态方法都是虚拟的。这意味着,如果UnrestrictedUser覆盖RestrictedUser中声明的某些方法,则会使用UnrestrictedUser版本,即使您通过RestrictedUser引用访问它也是如此。如果需要调用父类版本,可以通过调用RestrictedUser.variableName.someMethod()进行非虚拟调用。但是,你可能不应该经常使用它(最少的原因是它破坏了封装)。

答案 8 :(得分:0)

我也认为这个问题引出了另一个问题。你打算怎么称呼这种方法?似乎有一个Class Heirarchy,其中子类化引入新方法签名将需要在调用者中进行instanceof检查。

您是否可以更新您的问题以包含您可以调用这些方法的方案?也许我们能够提供更多帮助。

答案 9 :(得分:0)

John Calsbeek给出了技术答案。我想考虑一下设计问题。您要做的是阻止某些代码段或某些开发人员了解有关User类的任何信息。您希望他们将所有用户视为RestrictedUsers。

你这样做的方式是使用权限。您需要阻止有问题的开发人员访问User类。您可以将它放在一个包中并限制对包的访问。