【问题标题】:Why protected method is not accessible from subclass?为什么无法从子类访问受保护的方法?
【发布时间】:2015-08-14 14:09:26
【问题描述】:

考虑以下代码 sn-ps:

package vehicle;

public abstract class AbstractVehicle {
    protected int speedFactor() {
        return 5;
    }
}

package car;

import vehicle.AbstractVehicle;

public class SedanCar extends AbstractVehicle {
    public static void main(String[] args) {
        SedanCar sedan = new SedanCar();
        sedan
                .speedFactor();
        AbstractVehicle vehicle = new SedanCar();
        // vehicle //WON'T compile
        // .speedFactor();
    }
}

SedanCarAbstractVehicle 的子类,其中包含protected 方法speedFactor。如果它被同一个类引用,我可以调用方法speedFactor。当超类用于引用时,方法speedFactor 不可访问。

隐藏方法的原因是什么?

【问题讨论】:

  • 因为protected 对类本身(如private)及其子类可见。它不公开
  • protected 实例方法对类本身和子类的实例可见,但对子类中的静态方法不可见。
  • 但是 SedanCar 是 AbstractVehicle 的子类。
  • vehicle.speedFactor 将无法访问
  • 如果您在 SedanCar 类中创建了一个方法,则可以在该方法中调用 speedFactor。但不使用对象。

标签: java oop inheritance encapsulation access-specifier


【解决方案1】:

不同包中的子类不能使用超类引用访问超类的受保护方法和受保护变量。 在子类中访问超类的受保护数据的唯一方法是通过继承

下面是两个代码sn-ps

package nee;
import parentdata.Parent;

class Child extends Parent{

        public void testIt(){
        System.out.println(x);  // able to access protected x defined in Parent
        }

    }


package nee;
import parentdata.Parent;

        class Child extends Parent {

        public void testIt(){
        Parent p=new Parent();
        System.out.println(p.x) //  results in compile time error
        }

    }

在语言规范中 6.6.2.1 访问受保护成员

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

If the access is by a qualified name Q.Id, 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, where E is a Primary expression, or by a method invocation expression E.Id(. . .), where E is a Primary expression, then the access is permitted if and only if the type of E is S or a subclass of S.

如需深入了解,请访问 http://www.jot.fm/issues/issue_2005_10/article3.pdf

【讨论】:

    【解决方案2】:

    您的SedanCar 类与AbstractVehicle 类位于不同的包中。 protected 方法只能从同一个包或子类访问。

    如果是SedanCar

    SedanCar sedan = new SedanCar();
    sedan.speedFactor();
    

    您正在从同一个包中调用protected 方法:好的。 SedanCar 在包car 中,main() 方法在包car 中的一个类中(实际上是同一个类)。

    如果是AbstractVehicle

    AbstractVehicle vehicle = new SedanCar();
    vehicle.speedFactor();
    

    您尝试从另一个包调用protected 方法:不行。您尝试从中调用它的 main() 方法位于包 car 中,而 AbstractVehicle 位于包 vehicle 中。

    基本明白这个:

    您有一个 AbstractVehicle 类型的变量,它在另一个包 (vehicle) 中声明。它可能拥有也可能不拥有SedanCar 的动态类型。在您的情况下,它确实如此,但它也可以包含在另一个包中定义的任何其他子类的实例,例如在sportcar。由于你在包carmain()方法)中,你不能调用vehicle.speedFactor()(这是受保护的AbstractVehicle.speedFactor())。

    【讨论】:

    • 我有点惊讶它不能从扩展 AbstractVehicle 的类中调用。是的,它在一个静态方法中,但是该方法被一个扩展 AbstractVehicle 的类所包围。这有点道理,因为静态方法不参与继承,但它仍然让我感到惊讶。
    • @markspace 因为您尝试调用的方法不是SedanCar.speedFactor(),而是AbstractVehicle.speedFactor()。是的,实际上它将是 SedanCar.speedFactor() 但您指的是 AbstractVehicle.speedFactor() 这是另一个包中的 protected 方法。
    • 回到我的 SCJP 1.5 天,我曾经记得的一件事是警惕超类引用变量。现在看到这一点并不奇怪。
    • 我已经尝试在汽车包中创建另一个类,如果在 SedanCar 中覆盖了受保护的方法,我可以访问它。否则会受到限制。为什么会这样?
    • @harinewton 很可能是因为您指的是在同一包 (car) 中声明的被覆盖的 speedFactor() 方法,而不是在另一个包中声明的原始方法。如果这不令人满意并且您需要更多解释,请显示您的新代码。
    【解决方案3】:

    因为protected 对类本身(如私有)及其子类实例可见。它不公开。

    例如,

    package vehicles;
    
    public abstract class AbstractVehicle {
        protected int speedFactor() {
            return 5;
        }
    
        public int getSpeed() {
            return 10*speedFactor(); //accessing speedFactor() "privately"
        }
    }
    
    package vehicles.cars;
    
    public class SedanCar extends AbstractVehicle {
        @Override
        protected int speedFactor() { //overriding protected method (just to show that you can do that)
            return 10;
        }
    
        @Override
        public int getSpeed() {
            return 20*speedFactor(); //this is part of the instance (!!!) therefore it can access speedFactor() protected method too
        }
    }
    
    package vehicles.main;
    
    public class Main {
        public static void main(String[] args) {
            AbstractVehicle vehicle = new SedanCar();
            int speed = vehicle.getSpeed(); //accessing public method
            vehicle.speedFactor(); //cannot access protected method from outside class (in another package)
        }
    }
    

    静态main() 方法不是实例的一部分,这就是它无法访问受保护方法的原因。

    【讨论】:

    • w.r.t 你的最后一行 - “静态 main() 方法不是实例的一部分,这就是它无法访问受保护方法的原因。” - 即使我无法从非静态方法访问它。
    • @Ouney 如果它是SedanCar 中的非静态方法,那么您应该可以访问它。
    • 这也是我所期望的,但事实并非如此:(
    • @Ouney 很好,我提供的示例有效(尽管我确实需要将 Main 移动到另一个包以获取问题中提供的错误 - 因为 protected 也是 package private。)
    • 所以,您是说如果 SedanCar 与 AbstractVehicle 位于不同的包中,那么您可以安全地调用它(假设此代码在非静态方法中。) - AbstractVehicle vehicle = new SedanCar() ;vehicle.speedFactor() ??
    【解决方案4】:

    回到我的 SCJP for Java 1.5 天,我曾经记得的一件事是对超类引用变量保持警惕。现在看到这一点并不令人惊讶,而且这令人困惑的一件事是受保护的规则对子类或同一个包是可见的。如果它既是子类又是不同的包呢?

    如果你创建另一个包,然后做

    package yetAnotherPackage;
    
    import car.SedanCar;
    
    public class Main {
    
        public static void main(String[] args) {
            new SedanCar().speedFactor();
        }
    
    }
    

    你会看到的

    The method speedFactor() from the type AbstractVehicle is not visible
    

    看起来规则只是传播。只要您有一个子类并尝试访问子类的包中的受保护方法(或者如果没有子类,则为父类的包),您应该是好的。

    【讨论】:

      【解决方案5】:

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

      这就是你不能直接调用vehicle对象的main方法里面的方法的原因。

      见:https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

      【讨论】:

      • 但是代码有效,SedanCar 轿车 = new SedanCar();轿车.speedFactor()作品
      • 因为是在同一个java包中声明的
      猜你喜欢
      • 2014-04-17
      • 2015-11-04
      • 1970-01-01
      • 2016-05-25
      • 2012-11-25
      • 2013-04-06
      • 1970-01-01
      • 2014-05-01
      • 2017-12-14
      相关资源
      最近更新 更多