1

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 .

T-Bag
  • 10,916
  • 3
  • 54
  • 118
  • you can not override method and change it's signature - modifier. – matoni Sep 15 '17 at 10:49
  • 2
    @matoni, of course you can. Just not `public` > `private`, for example. – rorschach Sep 15 '17 at 10:50
  • @matoni it is wrong according to the Liskov Substitution principle to make the access modifier less-public than it was in the base class. But you can make it more-public in sub classes if you want. – Matthias Sep 15 '17 at 10:51
  • 1
    @matoni not sticky true: I believe Java supports covariant return types. For example, you could return a narrower type (subtype) than the base method. – Mike Strobel Sep 15 '17 at 10:51
  • @Matthias I did not say modifier can be changed nor it will be good to do something like this. – matoni Sep 15 '17 at 10:54
  • @MikeStrobel That's true, but you can not return completly different type. – matoni Sep 15 '17 at 10:54
  • 2
    The overriden method won't compile since its prohibited by the [standard](https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.3). Variables on the other hand can't be overriden. `Base.className` and `Derived.className` are two distinct variables. Thus they also can have different access-modifiers. You can verify this by assigning different values to `super.className` and `className` in `Derived`. They will have different values. –  Sep 15 '17 at 11:02
  • @GhostCat I think the question is not about changing of visibility of overriden method, but about using the variables with the same name in subclasses – Vlad Bochenin Sep 15 '17 at 11:04
  • @GhostCat - It is not duplicate please read the question carefully...:( – T-Bag Sep 15 '17 at 11:05

4 Answers4

3

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.

I.e. 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.

Community
  • 1
  • 1
2

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.

Vlad Bochenin
  • 3,007
  • 1
  • 20
  • 33
1

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();
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • I have edited the code if you can see , while we trying to access the private variables 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 . – T-Bag Sep 15 '17 at 10:59
  • Member variables in a subclass do not override member variables in the superclass. It does not work the same way as with methods. You'll just get two member variables: one in the superclass and one in the subclass. The one in the subclass hides (not overrides) the one in the superclass. – Jesper Sep 15 '17 at 11:03
  • This only answers the first part of the question, but completely ignores the part about "overriding" variables, which in fact isn't possible. –  Sep 15 '17 at 11:03
  • @Paul- If you could please share your views as a answer – T-Bag Sep 15 '17 at 11:04
  • @LoneWolf I intended to, but the answer got closed as dupe, which means I can't add an answer. I've added a comment to the question with the relevant details. –  Sep 15 '17 at 11:06
  • @Paul- GhostCat removed the duplicate now you can answer. – T-Bag Sep 15 '17 at 11:07
  • @Jesper should've checked the edited-tags, sry. Anyways, It'd still be cool if you could update the answer accordingly –  Sep 15 '17 at 12:57
0

While overriding super class’s method to a sub class, access level can be kept same or it should be wider/broader (i.e.; 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.