重写的方法和变量 - 不一致的行为

时间:2017-09-15 10:48:02

标签: java

以下代码在重写方法getName()上产生编译时错误,当可见性更改为private

这是可以理解的,但奇怪的是,重写的变量不会产生任何错误。

class Base {

    public String getName() {
        return "Base";
    }

    public String className = "Base";
}

class Derived extends Base {
    private String getName() { //Not compiling
        return "derived";
    }

    private String className = "Derived"; //Compiling successfully
}

public class Test{
  public static void main(String[] args) {
  System.out.println((new Derived()).className);// Gives compilation error 
    }

有人可以帮助我理解为什么会这样吗?

虽然我们尝试访问main()编译中的私有变量但是在方法中我自己将访问类型从公共减少到私有它成功编译它也应该在那里失败

4 个答案:

答案 0 :(得分:3)

the standard (§8.4.8.3)禁止使用较弱的访问修饰符覆盖方法:

  

覆盖或隐藏方法的访问修饰符(第6.6节)必须至少提供与重写或隐藏方法一样多的访问权限,如下所示:

     
      
  • 如果被覆盖或隐藏的方法是公共的,则覆盖或隐藏方法必须是公共的;否则,发生编译时错误。

  •   
  • 如果被覆盖或隐藏的方法受到保护,则必须保护或隐藏覆盖或隐藏方法;否则,发生编译时错误。

  •   
  • 如果重写或隐藏方法具有默认(包)访问权限,则覆盖或隐藏方法不得为私有方法;否则,发生编译时错误。

  •   

这确保了基类提供的任何方法也可以在同一个上下文中的派生类上调用。

无法覆盖变量。 Base.classNameDerived.className是两个不同的变量。因此,在Derived中拥有一个具有相同名称和不同访问修饰符的变量是完全有效的。

即。此代码将打印false

class Base{
    public String str = "hello";
}

class Derived extends Base{
    private String str = "whatever";

    public Derived(){
        super.str = "abc";
        str = "def";
    }

    void foo(){
        System.out.println(str.equals(super.str));
    }
}

public static void main(String[] args){
    new Derived().foo();
}

相关的jls-sections:

Field declarations (§8.3)

  

字段声明的范围和阴影在§6.3和§6.4中指定。

     

如果类声明了一个具有特定名称的字段,那么该字段的声明将被隐藏为隐藏超类中具有相同名称的字段的任何和所有可访问声明,以及该类的超接口。

     

在这方面,隐藏字段不同于隐藏方法(第8.4.8.3节),因为在字段隐藏中静态和非静态字段之间没有区别,而静态和非静态方法之间存在区别方法隐藏。

     

如果隐藏字段是静态的,则可以使用限定名称(第6.5.6.2节)访问隐藏字段,或者使用包含关键字super(第15.11.2节)或转换为超类类型的字段访问表达式来访问隐藏字段

     

在这方面,隐藏字段类似于隐藏方法。

     

如果字段声明隐藏了另一个字段的声明,则这两个字段不必具有相同的类型。

Shadowing (§6.4.1)

  

在整个d范围内,名为n shadows的字段或形式参数的声明d,名称为n的任何其他变量的声明,这些变量在d出现的范围内。

答案 1 :(得分:2)

你不能override字段,而只是hide字段。 这意味着您只需创建具有相同名称的新变量。

来自JLS Field declaration

  

如果类声明了一个具有特定名称的字段,那么该字段的声明将被隐藏为隐藏超类中具有相同名称的字段的任何和所有可访问声明,以及该类的超接口。

答案 2 :(得分:1)

无法使用限制性更强的访问说明符覆盖方法(例如,当超类中的方法为private时,public)。如果可以做到这一点,你就可以做一些奇怪的事情,比如调用一个不应该访问的private方法:

Derived object1 = new Derived();

// Will give an error, because getName() is private
String name1 = object1.getName();

Base object2 = new Derived();

// Should this be possible because getName() is public in Base?
// (Note that object2 is of type Base).
// But that would be strange, because the method is overridden
// in Derived, so we would be calling a private method here that
// should not be accessible from the outside!
String name2 = object2.getName();

答案 3 :(得分:0)

在将超类的方法重写为子类时,访问级别可以保持相同,也可以更宽/更宽(即,增加子类中重写方法的访问可见性)。

因此,如果您的基类方法是公共的,那么您不能将该方法重写为私有或受保护。