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