40

我看过各种关于受保护和包私有修饰符之间差异的文章。我发现这两个帖子之间存在矛盾的一件事

  1. “包私有”成员访问不是默认(无修饰符)访问的同义词吗?

    在这个接受的答案中说

    protected 修饰符指定该成员只能在其自己的包中访问(与 package-private 一样),此外,它的类在另一个包中的子类也可以访问。

  2. 为什么受保护的修饰符在 Java 子类中的行为不同?

    在这个接受的答案中说

    要满足受保护级别的访问,必须满足两个条件:

    • 这些类必须在同一个包中。
    • 必须有继承关系。

他们不矛盾吗?根据我对其他文章的理解,第一篇文章给出了protected == package-private + subclass in other package的正确答案。

如果这个陈述是正确的,那么为什么这个代码在第 17 行我的子类 Cat 上失败并出现以下错误消息

The method testInstanceMethod() from the type Animal is not visible 

我的超类和子类代码如下。

package inheritance;

public class Animal {

    public static void testClassMethod() {
        System.out.println("The class" + " method in Animal.");
    }
    protected void testInstanceMethod() {
        System.out.println("The instance " + " method in Animal.");
    }
}

package testpackage;

import inheritance.Animal;

public class Cat extends Animal{
        public static void testClassMethod() {
            System.out.println("The class method" + " in Cat.");
        }
        public void testInstanceMethod() {
            System.out.println("The instance method" + " in Cat.");
        }

        public static void main(String[] args) {
            Cat myCat = new Cat();
            Animal myAnimal = myCat;
            myAnimal.testClassMethod();
            myAnimal.testInstanceMethod();
        }
    }

请澄清上述代码失败的原因。那将非常有用。谢谢

4

3 回答 3

26

第一个答案基本上是正确的 -protected成员可以通过

  • 来自同一个包的类
  • 来自其他包的声明类的子类

但是,有一个小技巧:

6.6.2 受保护访问的详细信息

对象的受保护成员或构造函数可以从包外部访问,在该包中,仅由负责实现该对象的代码声明它。

这意味着来自其他包的子类不能访问protected其超类的任意实例的成员,它们只能在自己类型的实例上访问它们(其中 type 是表达式的编译时类型,因为它是编译时检查)。

例如(假设此代码在 中Cat):

Dog dog = new Dog();
Animal cat = new Cat();

dog.testInstanceMethod(); // Not allowed, because Cat should not be able to access protected members of Dog
cat.testInstanceMethod(); // Not allowed, because compiler doesn't know that runtime type of cat is Cat

((Cat) cat).testInstanceMethod(); // Allowed

这是有道理的,因为访问by的protected成员可能会破坏 的不变量,而可以安全地访问自己的成员,因为它知道如何确保自己的不变量。DogCatDogCatprotected

详细规则:

6.6.2.1 访问受保护成员

令 C 为声明受保护成员 m 的类。仅允许在 C 的子类 S 的主体内进行访问。此外,如果 Id 表示实例字段或实例方法,则:

  • 如果通过限定名称 Q.Id 进行访问,其中 Q 是 ExpressionName,则当且仅当表达式 Q 的类型是 S 或 S 的子类时,才允许访问。
  • 如果通过字段访问表达式 E.Id(其中 E 是 Primary 表达式)或通过方法调用表达式 E.Id(. . .)(其中 E 是 Primary 表达式)进行访问,则当且仅如果 E 的类型是 S 或 S 的子类。

6.6.2.2 对受保护构造函数的合格访问

令 C 为声明受保护构造函数的类,令 S 为在其声明中使用受保护构造函数的最内层类。然后:

  • 如果访问是通过超类构造函数调用 super(...) 或通过 E.super(...) 形式的限定超类构造函数调用,其中 E 是 Primary 表达式,则允许访问。
  • 如果访问是通过 new C(. . .){...} 形式的匿名类实例创建表达式或通过 E.new C(. . .){... 形式的限定类实例创建表达式进行的},其中 E 是主表达式,则允许访问。
  • 否则,如果访问是通过 new C(...) 形式的简单类实例创建表达式或通过 E.new C(...) 形式的限定类实例创建表达式,其中 E 是 Primary 表达式,则不允许访问。类实例创建表达式(不声明匿名类)只能从定义它的包内访问受保护的构造函数。

也可以看看:

于 2013-08-28T18:24:22.137 回答
5

在受保护的访问中,成员在同一个包中被访问,并且对于另一个包中的继承类成员也可以被访问。

在包访问中,可以访问同一包中的类的成员。包访问中无法访问其他包中的类成员。

于 2017-01-10T18:45:48.947 回答
0

您已经创建了一个 Cat 实例并将其转换为它的超类类型,即 Animal 类型。根据 Animal 类型,它的 testInstanceMethod 在同一包或任何子类型中可见。如果您没有强制转换为 Animal 类型,则代码将编译。

希望有帮助

./阿伦

于 2013-08-28T18:28:00.440 回答