为什么在另一个包中的子类不能访问受保护的方法?

20

我有两个不同包中的类:

package package1;

public class Class1 {
    public void tryMePublic() {
    }

    protected void tryMeProtected() {
    }
}


package package2;

import package1.Class1;

public class Class2 extends Class1 {
    doNow() {
        Class1 c = new Class1();
        c.tryMeProtected(); // ERROR: tryMeProtected() has protected access in Class1
        tryMeProtected();  // No error
    }    
}

我能理解为什么调用tryMeProtected()没有错误,因为Class2Class1继承了这个方法。

但是为什么一个Class2的对象不能使用 c.tryMeProtected(); 访问Class1对象上的这个方法呢?


1
这段代码并不会产生这个错误。方法tryMeProtected()根本不存在。 - user207421
1
你是否使用你发布的确切代码进行测试了?如果是的话,它可能无法正常工作,正如EJP所说,你有两个不同的名称:tryMePublic和tryMeProtected。 - CloudyMarble
8个回答

17

受保护的方法只能在子类中通过继承访问,对于包外的代码而言。因此第二种方法 tryMeProtected(); 是可行的。

下面的代码不会编译,因为我们没有调用受保护方法的继承版本。

 Class1 c = new Class1();
 c.tryMeProtected(); // ERROR: tryMeProtected() has protected access in Class1

点击此链接查看更详细的解释。


无论是在同一包内还是在外部,都可以访问(当然两者都可以),而不仅仅是在外部。 - Mik378
2
在同一个包中,它们可以通过单独的实例访问,因为它在Java中具有与默认可见性相同的可见性(除了子类可以继承甚至超出包范围)。 - Ankur Shanbhag
是的,同意,我倾向于这样的句子:“只有在不同的包中才有继承‘protected’方法的意义。” ;) - Mik378
@Mik378:是的,正确的。我已经修改了帖子中的语句。谢谢你指出来。 :-) - Ankur Shanbhag

14

我认为你误解了 packageprotected 可见性之间的区别。

package package1;

public class MyClass1 {
    public void tryMePublic() { System.out.println("I'm public"); }
    protected void tryMeProtected() { System.out.println("I'm protected"); }
    void tryMePackage() { System.out.println("I'm package"); }
}
  • tryMePublic 将在任何地方都可以访问。
  • tryMeProtected 将对每个子类和同一包中的每个类都可访问。
  • tryMePackage 将对同一包中的每个类可访问(如果子类位于不同的包中,则无法使用)。

同一包中的子类

package package1;

public class Class2 extends MyClass1 {
    public void doNow() {
        tryMePublic(); // OK
        tryMeProtected(); // OK
        tryMePackage(); // OK
    }    
}

不同包中的子类

package package2;

import package1.MyClass1;

public class Class3 extends MyClass1 {
    public void doNow() {
        MyClass1 c = new MyClass1();
        c.tryMeProtected() // ERROR, because only public methods are allowed to be called on class instance, whereever you are
        tryMePublic(); // OK
        tryMeProtected(); // OK
        tryMePackage(); // ERROR
    }    
}

4
你使用了两个不同的包,你不能通过直接继承来访问父类属性,而是通过在子类中声明的一个中间父实例来访问 (类似于组合). 这不是protected的工作方式。
只有直接继承才允许访问受保护的父级属性。
因此,您可以这样做:
public class Class2 extends Class1 {
    doNow() {
        tryMeProtected();  // No error since direct inheritance 
    }    
}

但从未是这样:
public class Class2 extends Class1 {
    doNow() {
        Class1 c = new Class1();
        c.tryMeProtected(); // this is not a direct inheritance! since `c`, although a parent one is an intermediate instance created in the child instance. => bad
    }    
}

实际上,这是protected关键字的一种特殊性质,它经常被误解。


@CloudyMarble 当然可以,但是使用protected关键字的继承概念的本质是针对不同的包。否则,默认的包作用域就足够了。当然:谁能做更多,谁就能做更少。 - Mik378
1
这是最好的答案,解释了OP所指出的困惑。 - Mára Toner

2

首先,你需要了解两件事:

1)类“X”在包“Y”中的protected成员函数可以被子类访问,即继承它的类(即使子类在不同于“Y”的包中)。这就是为什么,

tryMeProtected(); // Showed no error because this was called by class 2 that is the subclass.

2) 一个位于包 'Y' 中的类 'X' 的 protected 成员函数,如果它在其他包中,则无法被自身访问。

[ 简单的比喻:一只鸟将它的蛋放在巢1中,然后飞到了巢2。从巢2中,它无法访问存放在巢1中的蛋。] 同样地,如果一个类位于另一个包中,它也不能访问其成员函数(除非公开声明为public)。

这就是为什么:

c.tryMeProtected();  // Showed error because this was called by class 1 reference.
                     //  You can also think of it as class 1 cannot inherit itself.

1
根据Java Protected访问修饰符的定义,声明为protected的超类方法只能被其他包中的子类或受保护成员类所在包中的任何类访问。无法通过创建类对象来访问受保护的方法。因此,要访问Protected方法,必须扩展超类。(这解释了您的第二个调用是正确的)

0
你可以尝试这样做:-
public class Class2 extends Class1 {
    doNow() {
        
Class2 c = new Class2();
       
c.tryMeProtected(); // No error

tryMeProtected();  //  error
    }    

}


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Michael Katt

0

protected修饰符有两个特点:1.包私有;2.可以被其他包中的子类看到。现在关键的区别是:

MyClass1 c = new MyClass1();
    c.tryMeProtected();

tryMyProtected(); 

使用的是MyClass1的引用而不是继承。MyClass1在不同的包中,这段代码没有继承自MyClass1。


0

有两种方法可以实现

1. 通过创建子类的对象,然后访问父类的受保护方法。

包1

public class Class1 {
    protected void m1() {
        System.out.println("m1 called");
    }
}

包2

public class Class2 extends Class1 {

    public static void main(String[] args) {
        Class2 class2 = new Class2();
        class2.m1();
    }
}

2. 或者通过从子类直接调用该方法

例如:tryMeProtected();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接