简体   繁体   中英

Overridden methods and variables - Inconsistent behavior

Following code produces compile time error on overridden method getName() , when visibility is changed to private

This is understandable but strangely overridden variables does not produce any error .

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 
    }

Can some one help me understand why this is happening ?

While we trying to access the private variables in main() compilation fails but in method it self when I reduced the access type from public to private it compiles successfully it should have failed there as well .

Overriding a method with a weaker access-modifier is prohibited by the standard (§8.4.8.3) :

The access modifier (§6.6) of an overriding or hiding method must provide at least as much access as the overridden or hidden method, as follows:

  • If the overridden or hidden method is public, then the overriding or hiding method must be public; otherwise, a compile-time error occurs.

  • If the overridden or hidden method is protected, then the overriding or hiding method must be protected or public; otherwise, a compile-time error occurs.

  • If the overridden or hidden method has default (package) access, then the overriding or hiding method must not be private; otherwise, a compile-time error occurs.

This ensures that any method provided by the base-class can also be called on derived classes within the same context.

Variables can't be overriden. Base.className and Derived.className are two distinct variables. Thus it is perfectly valid to have a variable with the same name and different access-modifier in Derived .

Ie this code will print 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();
}

The relevant jls-sections:

Field declarations (§8.3) :

The scope and shadowing of a field declaration is specified in §6.3 and §6.4.

If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.

In this respect, hiding of fields differs from hiding of methods (§8.4.8.3), for there is no distinction drawn between static and non-static fields in field hiding whereas a distinction is drawn between static and non-static methods in method hiding.

A hidden field can be accessed by using a qualified name (§6.5.6.2) if it is static, or by using a field access expression that contains the keyword super (§15.11.2) or a cast to a superclass type.

In this respect, hiding of fields is similar to hiding of methods.

If a field declaration hides the declaration of another field, the two fields need not have the same type.

And Shadowing (§6.4.1) :

A declaration d of a field or formal parameter named n shadows, throughout the scope of d, the declarations of any other variables named n that are in scope at the point where d occurs.

You can not override field, but just hide it. That means that you just create new variable with the same name.

From JLS Field declaration

If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.

It is not possible to override methods with a more restrictive access specifier (for example, private when the method in the superclass is public ). If this would be possible, you would be able to do strange things, such as calling a private method that should not be accessible:

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();

While overriding super class's method to a sub class, access level can be kept same or it should be wider/broader (ie; to increase the access visibility of the overriding method in sub class).

So if your base class method is public then you can't override that method as private or protected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM