【问题标题】:Sub-class not able to access the protected method of super-class when the reference is of type super-class当引用是超类类型时,子类无法访问超类的受保护方法
【发布时间】:2018-07-15 09:46:16
【问题描述】:

父类

package p1;

public class A {
    protected void display() {
        System.out.println("Displayed");
    }
}

子类(在另一个包中)

package p2;

import p1.A;

public class B extends A {
    public static void main(String[] args) {
        A a = new B();
        a.display();
    }
}

在子类中,如果我写B a = new B(),它可以正常工作并显示“已显示”。

但是,上面写A a = new B()的子类中的代码,它会抛出编译时错误'display()' has protected access in 'p1.A'

我现在对受保护的访问修饰符感到困惑,因为当引用类型是父级时它是如何工作的?

【问题讨论】:

  • 您在静态方法中,而不是在 B 的方法中...在 B 中使用 foo() 进行测试 ;-)

标签: java access-modifiers


【解决方案1】:

这是相关的语言规范段落:

如果通过限定名称 Q.Id 或方法引用表达式 Q :: Id(第 15.13 节)进行访问,其中 Q 是 ExpressionName,则当且仅当表达式 Q 的类型为S 或 S 的子类。

(6.6.2.1. Access to a protected Member)

在您的情况下,QaSB。由于Q 的类型不是B 的子类,因此不允许访问。

有关为何引入此限制的说明,请参阅Checking Access to Protected Members in the Java Virtual Machine(搜索对受保护成员的要求

动机 限制受保护访问的背后是防止几乎任意访问 对象的受保护成员 [Yel02]。假设 m 是一个受保护的、非静态的 在 c 中声明的字段。没有限制,任何类 x 都可以读取 类 c 的任何对象的字段 m 的内容,使用以下技巧:定义一个 c 的子类 s(这个技巧只有在 c 不是 final 时才有效,因此是“几乎”副词 更多);在 s 中声明一个将类 c 的对象作为参数的方法,并且 返回其 m 字段的内容;并让 x 调用此方法。限制 on protected access 可以防止这种情况,因为 s 只能访问该字段 如果对象的类 o 满足 o ≤ s

【讨论】:

    【解决方案2】:

    A a = new B(); 不起作用,因为 a 的引用类型是 A 并且 A 不会将 display() 暴露给另一个包。

    举个例子:

    public void method(A a){ // defined in some class in a different package
       a.display(); // can't be called from different package as reference type is `A`
    }
    

    这里的编译器无法知道您是分配A 还是A 的子类型,因此它会退出。

    如果您暂时忘记您的示例并只关注我创建的方法会更容易理解,请注意我说它位于不同包中定义的类中。现在问问自己,我可以从不同的包中调用A 上的display(),显然它不是受保护的方法。

    【讨论】:

    • 什么?没有意义...您可以在 A 的所有子类上调用 display()
    • @Betlista 在子类中是,但不是通过创建具有基类型引用类型的变量
    • 当然可以,请看我的 C 示例...您只需要在调用它的地方有可见性即可。
    • @Betlista 在您的示例中a 的引用类型是B,编译器知道它继承自A,因此它可以工作
    • 再次检查,没有办法,A a = new A(); 中的a 拥有 B...(类 C)的实例。
    【解决方案3】:

    正如我在评论中所写,您可以这样做:

    package p2;
    
    import p1.A;
    
    public class B extends A {
    
        public static void main(String[] args) {
            B a = new B();
            a.foo();
        }
    
        public void foo() {
            display(); // works
            super.display(); // same, but more wordy
            A a = (A)this;
            a.display(); // NOT working !!!
        }
    
        public void bar(A a) {
            a.display(); // NOT working !!!
        }
    }
    

    display(),可以从子类调用(静态方法不是成员方法),也可以像这样在同一个包中调用

    package p1;
    
    public class C {
    
        public static void main(String[] args) {
            A a = new A();
            a.display();
        }
    
    }
    

    嗯,那些

    a.display(); // NOT working !!!
    

    对我来说不是很清楚,请参阅discussion

    【讨论】:

    • 但即使我使用静态方法,我也会在引用上调用显示方法。那么 B a = new B() 是如何工作的!
    • 我知道这有点令人困惑,static 方法不是 member 方法,这很重要。表 here 是为 members...
    【解决方案4】:

    受保护的方法只能通过包外的子类中的继承来访问

    display 将可供同一包中的每个子类和每个类访问。

    display 将可供同一包中的每个类访问(如果在不同的包中,则子类中不可用)

    A a = 新 B();将不起作用,因为 a 的引用类型是 A 并且 A 不会将 display() 暴露给另一个包。

    以下代码将起作用:

    package p2;
    
    import p1.A;
    
    public class B extends A {
        public static void main(String[] args) {
            B a = new B();
            a.demo();
        }
    
        public void demo(){
            display();
        }
    }
    

    【讨论】:

      【解决方案5】:

      我认为从链接:http://tutorials.jenkov.com/java/access-modifiers.html重要声明是

      protected 访问修饰符提供与默认访问修饰符相同的访问权限,此外子类可以访问超类的受保护方法和成员变量(字段)。

      这里,子类仅表示子类引用类型。对于父引用类型,剩下的唯一选项是 public。

      这是一种不言而喻的潜规则。甚至 IDE 也建议将其更改为 public 以通过父母的参考访问。奇怪!

      【讨论】:

        猜你喜欢
        • 2017-12-09
        • 2011-07-09
        • 2012-12-01
        • 2013-05-27
        • 2013-06-08
        • 1970-01-01
        • 2015-10-01
        • 2011-02-18
        • 2011-05-18
        相关资源
        最近更新 更多