代码之家  ›  专栏  ›  技术社区  ›  tatsuhirosatou

Java中的UP浇铸隐藏子类方法和字段吗?

  •  7
  • tatsuhirosatou  · 技术社区  · 16 年前

    在我写的节目里,我有一节课 RestrictedUser 和班 User 这是从 RestrictedUser. 我试图通过强制转换来隐藏用户特定的方法 限制用户 但是,当我执行强制转换时,用户方法仍然可用。另外,当我运行调试器时,变量的类型显示为 用户 .

    RestrictedUser restricted = regularUser;
    

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

    谢谢

    10 回复  |  直到 7 年前
        1
  •  16
  •   John Calsbeek    16 年前

    如果要尝试运行此代码:

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

    你会得到一个编译错误。这在任何方式、形状或形式上都是不安全的,因为即使你要通过 RestrictedUser 例如,对于另一种方法,该方法可以做到这一点:

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

    你的“受限”用户不再受到限制了。

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

        2
  •  3
  •   Peter Kelley    16 年前

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

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

        3
  •  3
  •   tpdi    16 年前

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

    静态类型是引用的类型。当您从派生到基进行向上转换时,您告诉编译器,就您和它所知道的,指向的是基。也就是说,您保证指向的对象要么是空的,要么是基,或者是从基派生的。也就是说,它有基础的公共接口。

    然后,您将无法调用在派生(而不是在基)中声明的方法,但当您调用由派生重写的任何基方法时,将获得派生的重写版本。

        4
  •  3
  •   user85421    15 年前

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

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

        5
  •  2
  •   Community CDub    8 年前

    Peter's answer John's answer 是正确的:如果对象的实际类型(客户端代码可以强制转换到的类型、调用反射方法等)仍然可用,则强制转换静态类型将不起任何作用。你必须以某种方式掩饰真实的类型(正如彼得的回答所提到的)。

    实际上有一种模式可以做到这一点:看看 unconfigurable* 方法在 Executors 类,如果您有权访问JDK源代码。

        6
  •  2
  •   Hugo    16 年前

    爪哇

    在爪哇,你永远不能“隐藏”某物。 编译器可以忘记关于您所拥有的特定实例的详细信息。(就像它是什么子类一样)调试器比编译器知道的更多,因此它将告诉您当前实例的实际类型,这可能是您正在经历的。

    面向对象程序设计

    听起来你在写逻辑,需要知道你所使用的对象是什么类型的,这在OOP中是不推荐的,但通常是出于实际原因而需要的。

    限制形容词

    正如我在对这个问题的评论中所说,您应该清楚这里的基类是什么。直观上受限制的用户应该是用户的子类,因为名称建议使用更专门的类型。(名称上有一个附加的活动形容词。)在您的情况下,这是特别的,因为这是一个限制性形容词,可以让您将用户作为restricteeduser的一个子类,完全可以。我建议将这两个词重新命名为:basicuser/userwithid和nonrestrictedurer,以避免使用限定形容词,这是这里的混淆部分。

        7
  •  1
  •   C. K. Young    16 年前

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

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

    您可能无法说出 run (为了直接向其强制转换),但您仍然可以使用 getClass() ,你可以打电话 secretSquirrel 使用反射。(即使 分泌松鼠 是私人的,如果你没有 SecurityManager 或者,如果设置为允许访问,反射也可以调用私有方法。)

        8
  •  0
  •   James    16 年前

    我想你可能做了一个糟糕的命名选择:我想 RestrictedUser 将是 User 不是反过来,所以我会用 UnrestrictedUser 作为子类。

    如果你把一个 无限制用户 到A 限制用户 ,您的引用将只能访问 限制用户 . 但是,如果你把它降回到 无限制用户 ,您将可以访问所有方法。因为Java对象知道它们实际上是什么类型,所以实际上是一个 无限制用户 无论您使用的是哪种类型的引用(甚至是 Object )语言的一部分是不可避免的。但是,您可以将其隐藏在某种代理之后,但在您的情况下,这可能会破坏目标。

    此外,在Java中,所有非静态方法默认都是虚拟的。这意味着如果 无限制用户 重写中声明的某些方法 限制用户 然后 无限制用户 将使用版本,即使您通过 限制用户 参考文献。如果需要调用父类版本,可以通过调用 RestrictedUser.variableName.someMethod() . 但是,您可能不应该经常使用它(最不重要的原因是它破坏了封装)。

        9
  •  0
  •   Nathan Feger    16 年前

    我也认为这个问题引出了另一个问题。你打算如何调用这个方法?似乎有一个类继承性,在这个继承性中,子类化引入了新的方法签名,需要在调用方中进行实例检查。

    您是否可以更新您的问题以包括一个场景,您将在其中调用这些有问题的方法?也许我们能提供更多帮助。

        10
  •  0
  •   DJClayworth    16 年前

    约翰·卡尔斯贝克给出了技术答案。我想考虑一下设计问题。您要做的是防止代码的某些部分,或者一些开发人员知道关于用户类的任何信息。您希望他们将所有用户视为受限制的用户。

    这样做的方法是使用权限。您需要防止有问题的开发人员访问用户类。您可以通过将它放在包中并限制对包的访问来实现这一点。

    推荐文章