Java使用父类型的对象引用访问不同包中子类中的受保护成员

Java使用父类型的对象引用访问不同包中子类中的受保护成员,java,inheritance,polymorphism,protected,Java,Inheritance,Polymorphism,Protected,我在两个单独的文件中有以下代码 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() {

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

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()
的调用将是对
Tadpole
ribbit()
实现的调用。第二个
蝌蚪
对象,创建并分配给
青蛙
参考。但是,调用
ribbit()
会导致编译器错误


我知道,如果您在子类中创建子类对象,并将其分配给子类包之外的超类引用,并尝试调用超类方法,这是不允许的。但是在这种情况下,多态性不应该让对象引用“f”调用
Tadpole
ribbit()
方法吗,因为
Tadpole
对象被分配给它了?为什么这会导致编译器错误,为什么这是不允许的?

受保护的
限制对
或子类的访问,只要子类声明为子类的类型而不是超类


e、 g.如果
青蛙
蝌蚪
在不同的
包装中
青蛙f=新蝌蚪()ribbit
方法,则code>不起作用,但
Tadpole f=new Tadpole()确实

这与有关访问受保护的
类成员的规则有关。有关详细信息,请参见Java语言规范,具体如下:

设C为声明
受保护的
成员的类。仅允许在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
子类的主体中,如果
x
Frog
的子类(
x
不能声明为
Frog
),则只能访问
x.ribbit()

此限制存在于受保护的
成员上,因为否则,假设
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
实际上是指
Frog
还是指
Tadpole
。编译器在这里所做的只是执行
protected
修饰符的规则,仅此而已

编辑2:


根据下面的评论,如果我们将
revealFieldValue(蝌蚪蛙)
方法更改为
revealFieldValue(蝌蚪蛙)
,那么
revealFieldValueOfParent(Frog-Frog)
方法实际上会显示受保护的
的值,但您也可以对私人成员执行该显示技巧(即类似于getter方法)。子类的责任就是知道它在做什么。

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

当您尝试从Frog对象调用方法时,在编译时,它只查找Frog类方法原型,并声明为protected,这在Frog类之外是不可见的。
希望这能帮助您

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

感谢您的所有回复。根据一些回复,我想我已经弄明白了为什么它不能编译。我将发布一个单独的回复,以使其完全清楚,并将澄清哪些内容与哪些内容相关,因为我觉得许多回复只包含解释的片段

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

同样,这是令人困惑的,因为蝌蚪是青蛙的一个子类,所有的叫声都是在蝌蚪子类中发出的。但需要注意的是,调用本身是使用对象引用“f”进行的,该对象引用是Frog类型,试图从Tadpole访问受保护的方法,而Frog不是Tadpole的子类。

Prote