Java中的protected和package-private访问修饰符之间的区别?

eag*_*arn 34 java protected access-modifiers package

我已经看过有关protected和package private修饰符之间差异的各种文章.有一件事我发现这两个帖子之间存在矛盾

  1. "包私有"成员访问是否与默认(无修饰符)访问同义?

    在这个被接受的答案说

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

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

    在这个被接受的答案说

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

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

他们不矛盾吗?根据我对其他文章的理解,第一篇文章给出了正确的答案,即在其他包中保护== package-private + subclass.

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

The method testInstanceMethod() from the type Animal is not visible 
Run Code Online (Sandbox Code Playgroud)

我的超级和子类代码如下.

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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

请说明上述代码失败的原因.那将非常有用.谢谢

axt*_*avt 25

第一个答案基本上是正确的 - 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
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为访问protected的成员Dog通过Cat可以打破不变Dog,而Cat可以访问自己的protected安全成员,因为它知道如何保证自己的不变量.

详细规则:

6.6.2.1访问受保护的成员

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

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

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

设C是声明受保护构造函数的类,并让S成为其声明中使用受保护构造函数的最内层类.然后:

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

也可以看看:

  • @eagertoLearn - 背后的想法是一个子类总是可以使方法更易于访问但不易被访问; 这样,子类仍然符合超类合同.换句话说,投射到超类应该永远不会让你访问更多的方法. (2认同)

小智 5

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

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

  • 阅读“接受的答案”让我的眼睛流血……我不是语言语法律师(也不会在电视上扮演)所以我实际上很欣赏这个“更简洁”的答案。 (6认同)
  • 这个问题已经三年多了,已经有一个公认的答案了。您的回答也没有为已经接受的答案添加任何有价值的内容,因此我真的认为您的回答没有任何意义(无意冒犯)。 (2认同)