【问题标题】:Help to understand the issue with protected method帮助理解受保护方法的问题
【发布时间】:2011-01-15 17:18:44
【问题描述】:

我正在阅读 Sybex Complete Java 2 Certification Study Guide 2005 年 4 月 (ISBN0782144195)。本书适用于希望通过java认证的java开发者。

在关于访问修饰符(以及其他修饰符)的一章之后,我发现了以下问题(#17):

判断对错:如果类 Y 扩展 X班,两个班在 不同的包,X 类有一个 称为 abby() 的受保护方法,然后 Y 的任何实例都可以调用 abby() Y 的任何其他实例的方法。

这个问题让我有点困惑。

据我所知,您可以在同一类(或子类)的任何变量上调用受保护的方法。您不能在层次结构中高于您的变量上调用它(例如您实现的接口)。

例如,你不能仅仅因为你继承它而克隆任何对象。

但是这些问题没有提到变量类型,只提到了实例类型。

我有些疑惑,回答“是”。

书中的答案是

错误。从不同包中的超类继承受保护方法的对象可以在其自身上调用该方法,但不能在同一类的其他实例上调用。

这里没有关于变量类型,只有实例类型。

这很奇怪,我不明白。

谁能解释这里发生了什么?

【问题讨论】:

  • “我正在阅读 Sybex.Complete.Java.2.Certification.Study.Guide.Apr.2005.ISBN0782144195。”感谢海盗湾!

标签: java certificate access-modifiers protected


【解决方案1】:

判断对错:如果类 Y 扩展类 X,这两个类在不同的包中,并且类 X 有一个名为 abby() 的受保护方法,那么 Y 的任何实例都可以调用任何其他实例的 abby() 方法Y。

“错误。从不同包中的超类继承受保护方法的对象可能会在其自身上调用该方法,但不能在同一类的其他实例上调用该方法”。

让我们把它写下来,就像BalusC 所做的那样,并向 Y 添加一个调用 Y 的任何其他实例的 abby() 的方法:

package one;
public class X {
    protected void abby() {
    }
}

package other;
import one.X;
public class Y extends X {
    public void callAbbyOf(Y anyOther) {
        anyOther.abby();
    }
}

Y 可以调用它所引用的任何 Y 实例的 abby() 方法。 所以书中的答案是明显错误的。 Java 没有特定于实例的作用域(与具有实例私有作用域的 Scala 不同)。

如果我们尝试仁慈,也许问题的意思是说“any Y 的其他实例”,它可以访问 恰好在内存中的 Y 的任何实例的方法 - 这是不可能的,因为 Java 没有直接的内存访问。但在这种情况下,这个问题的措辞非常糟糕,你甚至可以回答:“错误。你不能调用不同 JVM 上的实例、已被垃圾回收的实例或 JVM 上死掉的实例的方法。一年前等等。”

【讨论】:

    【解决方案2】:

    来自The Java Language Specification

    6.6.2.1 Access to a protected Member

    令 C 为声明受保护成员 m 的类。仅允许在 C 的子类 S 的主体内访问。此外,如果 Id 表示实例字段或实例方法,则:

    • 如果通过限定名称 Q.Id 进行访问,其中 Q 是 ExpressionName,则当且仅当表达式 Q 的类型是 S 或 S 的子类时才允许访问。
    • 如果通过字段访问表达式 E.Id(其中 E 是主表达式)或通过方法调用表达式 E.Id(...)(其中 E 是主表达式)进行访问,则允许访问当且仅当 E 的类型是 S 或 S 的子类。

    所以受保护的成员在 S 的所有实例中都可以访问,而你书中的答案是错误的。

    【讨论】:

      【解决方案3】:

      这个问题的措辞似乎很糟糕 - 并询问了一个非常罕见的边缘情况(我什至不确定 SCJP 测试是否涵盖了这种情况)。它的措辞方式使您的答案正确而给定的答案不正确。编写一个类似的结构并轻松运行它可以证明这一点......

      package inside;
      
      public class Base {
      
          private String name;
      
          public Base(String name)  {
              this.name = name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          protected String abby(String name) {
              String old = this.name;
              this.name = name;
              return old;
          }
      }
      
      
      
      
      package outside;
      import inside.Base;
      
      public class Another extends Base {
      
          public Another(String name) {
              super(name);
          }
      
          public String setAnother(Another another, String hack) {
              return another.abby(hack);
          }
      
          public static void doCrazyStuff() {
              Another one = new Another("one");
              Another two = new Another("two");
      
              one.abby("Hi one"); 
              two.abby("Hi two");
              one.setAnother(two, "Hi two from one");
      
              System.out.println("one = " + one.getName());
              System.out.println("two = " + two.getName());
      
          }
      
          public static void main(String[] args) {
              Another.doCrazyStuff();
          }
      }
      

      【讨论】:

        【解决方案4】:

        因为变量类型在这里是无关紧要的,直到它在问题的上下文中是“理智的”。由于方法abby() 属于X(并且Y 继承它),因此声明引用Y 实例的类型变量无关紧要:它可以是XY。可访问abby(),我们可以通过两个变量调用它:

        X myY1 = new Y();
        myY1.abby();
        
        Y myY2 = new Y();
        myY2.abby();
        

        【讨论】:

          【解决方案5】:

          判断对错:如果类 Y 扩展类 X,这两个类在不同的包中,并且类 X 有一个名为 abby() 的受保护方法,那么 Y 的任何实例都可以调用任何其他实例的 abby() 方法Y。

          让我们描绘一下吧。

          X 班:

          package one;
          public class X {
              protected void abby() {}
          }
          

          Y 班:

          package other;
          public class Y extends X {}
          

          测试用例:

          public static void main(String[] args) {
              Y y1 = new Y();
              Y y2 = new Y();
              Y y3 = new Y();
              // ...
          }
          

          现在重读这个问题:y1 可以在y2y3 等上调用abby() 吗?在y1 上调用abby() 是否也会调用y2y3 等的那些?

          对于未来的问题,请尝试拿起笔和纸并逐字解释问题。这类模拟题有很多漏洞。

          【讨论】:

          • 如果你在 Y 内部创建一些方法来接收任何 Y 类型的对象并尝试调用 abby() - 一切都会好起来的。我试过了。
          • 问题/示例是否说明了有关添加新方法的内容,还是您自己发明的?你不应该那样做。上下文真的不应该比问题本身更进一步。否则很容易对所有问题说“是”。
          • 但是一个实例如何在没有方法的情况下调用另一个实例上的某个方法呢? O_o
          • 孩子,确实是可能,但您只需要以是或否来回答整个问题,而不是提出改变问题答案的解决方案。您真的不应该发明/添加更多根本没有被唯一问题涵盖的代码。这就是这类问题的全部意义所在。
          • 在这种情况下,问题令人困惑,不应在认证考试中使用。我希望是的。
          【解决方案6】:

          我几乎可以肯定这个问题的意思是:

          “Y 的任何实例都可以调用 X 的任何其他实例的 abbey() 方法”(不是 Y)。

          在这种情况下,它确实会失败。借用上面另一个答案的例子,如下:

          package one;
          public class X {
              protected void abby() {
              }
          }
          
          package other;
          import one.X;
          public class Y extends X {
              public void callAbbyOf(X anyOther) {
                  anyOther.abby();
              }
          }
          

          将无法编译。

          Java 语言规范在这里解释了原因:http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.2

          6.6.2.1 访问受保护成员

          设 C 是一个类 声明了受保护的成员 m。使用权 只允许在一个体内 C 的子类 S。此外,如果 Id 表示实例字段或实例 方法,则:如果访问是由 限定名称 Q.Id,其中 Q 是 ExpressionName,则访问权限为 当且仅当 表达式 Q 是 S 或 S. 如果访问是通过字段访问 表达式 E.Id,其中 E 是 Primary 表达式,或通过方法调用 表达式 E.Id(. . .),其中 E 是 主表达式,则 访问为 当且仅当 E 的类型是允许的 是 S 或 S 的子类。(强调我的)。

          【讨论】:

            猜你喜欢
            • 2020-11-07
            • 2020-07-26
            • 1970-01-01
            • 1970-01-01
            • 2011-05-14
            • 2011-05-22
            • 1970-01-01
            • 1970-01-01
            • 2020-03-31
            相关资源
            最近更新 更多