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

Java access to protected member in subclass in different package, using object reference of parent type

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

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() 会导致编译器错误。

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

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

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

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

Let C be the class in which a protected member is declared. Access is permitted only within the body of a subclass S of C.

In addition, if Id denotes an instance field or instance method, then:

  • If the access is by a qualified name Q.Id or a method reference expression Q :: Id (§15.13), where Q is an ExpressionName, then the access is permitted if and only if the type of the expression Q is S or a subclass of S.

  • If the access is by a field access expression E.Id, or a method invocation expression E.Id(...), or a method reference expression E :: Id, where E is a Primary expression (§15.8), then the access is permitted if and only if the type of E is S or a subclass of S.

所以在 Frog 的子 class 的正文中,如果 xsub[=63,您只能访问 x.ribbit() =] of Frogx 不能声明为 Frog)。

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

public class Frog {
    protected int a = 1;

    ...
}

然后就可以在Frog的subclass中定义一个public方法:

public class TadPole extends Frog {

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

然后任何其他(不相关的)class 都可以通过将 Frog 传递给 subclass:

的方法来访问该字段
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:

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

Protected Access Modifier - 在 superclass 中声明为 protected 的变量、方法和构造函数只能由其他包中的 subclass 或任何 class 访问受保护成员的包裹class.

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

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

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

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

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

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