【问题标题】:Java access to protected member in subclass in different package, using object reference of parent typeJava访问不同包中子类中的受保护成员,使用父类型的对象引用
【发布时间】:2015-08-13 18:17:49
【问题描述】:

我在两个单独的文件中有以下代码。

package animal;

public class Frog
{
    protected void ribbit()
    {
        System.out.println("In Frog class!");
    }
}





package other;

import animal.*;

public class Tadpole extends Frog

{
    protected void ribbit()
    {
        System.out.println("In Tadpole class!");
    }

    public static void main(String[] args)
    {
        Tadpole t = new Tadpole();
        t.ribbit();

        Frog f = new Tadpole();
        f.ribbit(); // Does not compile
    }
}

分配给Tadpole 类型的第一个Tadpole 对象显然编译得很好,对ribbit() 的调用将是Tadpoleribbit() 实现。创建并分配给Frog 引用的第二个Tadpole 对象。但是,对ribbit() 的调用会导致编译器错误。

我知道,如果你在子类中创建一个子类对象,并分配给一个在子类的包之外的超类引用,并尝试调用超类的方法,这是不允许的。但是在这种情况下,由于分配了Tadpole 对象,多态性不应该使对象引用“f”调用Tadpoleribbit() 方法吗?为什么这会导致编译器错误,为什么不允许这样做?

【问题讨论】:

    标签: java inheritance polymorphism protected


    【解决方案1】:

    这与访问protected 类成员的规则有关。有关详细信息,请参阅 Java 语言规范中的this section,具体而言:

    令 C 为声明 protected 成员的类。只允许在 C 的子类 S 的主体内访问。

    另外,如果Id表示一个实例字段或实例方法,那么:

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

    • 如果访问是通过字段访问表达式 E.Id,或方法调用表达式 E.Id(...),或方法引用表达式 E :: Id,其中 E 是主表达式 ( §15.8),则当且仅当 E 的类型是 S 或 S 的子类时才允许访问

    所以在Frog 的子类的主体内,如果xFrog子类,您只能访问x.ribbit()x 不能被声明为@987654329 @)。

    此限制存在于protected 成员上,否则,假设Frog 有一个受保护的int 字段:

    public class Frog {
        protected int a = 1;
    
        ...
    }
    

    然后可以在Frog的子类中定义public方法:

    public class TadPole extends Frog {
    
        public int revealFieldValueOfParent(Frog frog) {
            return frog.a;  // imagine this was OK
        }
    }
    

    然后任何其他(不相关的)类都可以通过将Frog 传递给子类的方法来访问该字段:

    public class SomeOtherClass {
    
        public static void main(String[] args) {
             TadPole tadpole = new TadPole();
             Frog frog = new Frog();
             int revealedValue = tadpole.revealFieldValueOfParent(frog);
             // print revealedValue
        }
    }
    

    编辑:

    此编译器错误与多态性无关。与对象的实际类型相关的多态性是运行时方面,编译器不会尝试在运行时考虑变量f 是否实际上是指FrogTadpole。编译器在这里所​​做的只是强制执行protected 修饰符的规则,仅此而已。

    编辑 2:

    根据下面的 cmets,如果我们将 protected 更改为 revealFieldValue(TadPole frog)revealFieldValueOfParent(Frog frog) 方法实际上会显示它的值,但您也可以使用 private 来完成这个揭示技巧成员(即类似于 getter 方法)。知道它在做什么真的会成为子类的责任。

    【讨论】:

    • 谢谢@manouti。你知道这条规则背后的原因吗? Java 的创建者对此有何打算?
    • @CodeMonkey 这应该被标记为正确答案:)
    • @CodeMonkey 更新了答案。
    • 感谢@manouti,这解释了为什么 Java 是按照您发布的规则设计的。请在这篇文章中查看我对这个问题的回答,看看你是否同意我对这个问题的解释。我相信这与青蛙实际上不是蝌蚪的子类有关。
    • 我们可以通过改变引用类型来揭示值 public class TadPole extends Frog { public int revealFieldValueOfParent(TadPole frog) { return frog.a; // 我们仍然可以这样做 } }
    【解决方案2】:

    只是为了简单理解,方法覆盖是运行时特性。这是在运行时获得的,编译器在编译期间不关心这一点。所以你的代码必须符合编译器。因此,您的编译失败,因为在编译期间无法从其他包访问该方法(尽管它在运行时可以使用,因为它继承了 Frog 类)。

    【讨论】:

      【解决方案3】:

      protected 限制对Classpackage 或子类的访问,只要子类被声明为子类的类型而不是超类。

      例如如果FrogTadpole 在不同的packages 中,如果你想访问Frogribbit 方法,Frog f = new Tadpole(); 不起作用,但Tadpole f = new Tadpole(); 可以

      【讨论】:

      • Protected 也允许子类访问@CodeMonkey 但不允许访问其他包
      • 如果代码在同一个package 中或者Frogribbit 被声明为public,我可以编译代码
      • “受保护的访问修饰符可在包内和包外访问,但只能通过继承访问。”在这种情况下,它是通过继承,因为 Tadpole 扩展了 Frog。 javatpoint.com/access-modifiers
      • @nLee,如果它们在同一个包中或者如果 ribbit 是公共的,则代码将编译。但是,这个问题是针对 ribbit() 受保护且类位于单独的包中并且在分配有子类对象的超类引用上调用 ribbit() 方法的特殊情况。
      • @CodeMonkey 是的,但蝌蚪和青蛙在不同的包中。在此处查看更多信息:docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
      【解决方案4】:

      受保护的访问修饰符 - 在超类中声明为受保护的变量、方法和构造函数只能由其他包中的子类或受保护成员类的包中的任何类访问。

      【讨论】:

        【解决方案5】:

        当您尝试从 Frog 对象调用方法时,在编译时它只查找 Frog 类方法原型,并且它被声明为受保护,在 Frog 类之外不可见。 希望对你有帮助

        【讨论】:

        • 这里没有创建 Frog 对象。只有两个 Tadpole 对象,其中第二个被分配给 Frog 类型的对象引用...
        【解决方案6】:

        感谢您的所有回复。根据一些回复,我想我已经弄清楚为什么这不能编译。我发布了一个单独的回复以使其完全清楚,并将澄清哪些事情无法访问,因为我觉得许多回复只包含解释的 sn-ps。

        Frog f = new Tadpole();
        f.ribbit(); // does not compile
        

        上面的代码无法编译,因为虽然 Tadpole 对象是在 Tadpole 类本身中创建的并且正在调用 Tadpole 的 ribbit() 方法,但它是使用 Frog 对象引用来调用的。这里我们有一个分配给超类引用的子类,并且由于多态性,Frog 对象引用“f”将尝试调用 Tadpole 的 ribbit() 方法。但是,因为 ribbit() 是受保护的并且 Frog 不是 Tadpole 的子类,所以 Frog 类型的对象引用无法访问 Tadpole 中定义的受保护方法。这就是代码无法编译的原因。 p>

        同样,这令人困惑,因为 Tadpole 是 Frog 的子类,所有调用都在子类 Tadpole 中进行。不过要注意的是,调用本身是使用 Frog 类型的对象引用“f”进行的,它试图从 Tadpole 访问受保护的方法,而 Frog 不是 Tadpole 的子类。

        【讨论】:

        • 编译器错误与多态无关。多态性是运行时方面,如果f 在运行时实际上是FrogTadpole,编译器并不“关心”。编译器在这里所​​做的只是强制执行protected 修饰符的规则,仅此而已。
        • 并非所有的多态性都是运行时的,但最重要的是您试图实现的运行时多态性。而且您对作为编译时间的运行时多态性感到困惑,并期望调用能够正确编译,尽管该方法在调用类中不可见。
        猜你喜欢
        • 1970-01-01
        • 2018-10-30
        • 2016-07-05
        • 2020-05-27
        • 2016-05-06
        • 2012-12-01
        • 2018-09-22
        • 2016-10-01
        • 2016-06-23
        相关资源
        最近更新 更多