17

I have a class which has a method whose access specifier by default is public. Now, I would like to extend this class in a subclass and I want to override this method to have access specifier "private". When compiling this code, I am getting a compilation error:

"attempting to assign weaker access privileges".

Could somebody please explain to me what is wrong with assigning weaker privileges in a subclass?

Here is the code that caused the compilation error:

class Superclass 
{
    void foo() 
    {
        System.out.println("Superclass.foo");
    }
}

class Subclass extends Superclass 
{
    private void foo() 
    {
        System.out.println("Subclass.foo");
    }
}
beatgammit
  • 19,817
  • 19
  • 86
  • 129
syam
  • 267
  • 1
  • 4
  • 11
  • 3
    Interesting question! This very case *is* allowed in C++. – quamrana Jul 07 '13 at 07:44
  • 1
    "I have a class which has a method whose access specifier by default is public". Actually the default access specifier is not public. If you did't mentioned any specific access specifier then it is 'default' :). – Niju Jul 07 '13 at 07:57
  • @quamrana And what happens in C++ when you acces an object through a superclass-typed variable? The compiler surely lets it through... does it error out at runtime? – Marko Topolnik Jul 07 '13 at 08:06
  • @StephenC +1 I was trying to tease out that statement from quamrana :) – Marko Topolnik Jul 07 '13 at 08:10
  • 1
    In the case of C++ and the example above (lets say public in the base class, private in the derived class) access only depends on the pointer you have at hand. There are no runtime errors, only compile-time errors (accessing private method from outside the class). – quamrana Jul 07 '13 at 13:30

9 Answers9

34

The short answer is that it is not allowed because it would break type substitutability; see also the Liskov Substititution Principle (LSP).

The point is that polymorphism in Java (and other programming languages) relies on you being able to treat an instance of a subclass as if it was an instance of the superclass. But if the method is restricted in the subclass, you find that the compiler cannot figure out whether the access rules allow a method to be called ...

For instance, lets assume that your example code was legal:

// Assume this code is in some other class ...

SuperClass s1 = new SuperClass();

s1.foo();                          // OK!

SuperClass s2 = new Subclass();

s2.foo();                          // What happens now?

SuperClass s3 = OtherClass.someMethod();

s3.foo();                          // What happens now?

If you base the decision on whether s2.foo() is allowed on the declared type of s2, then you allow a call to a private method from outside the abstraction boundary of Subclass.

If you base the decision on the actual type of the object that s2 refers to, you cannot do the access check statically. The s3 case makes this even clearer. The compiler has absolutely no way of knowing what the actual type of the object returned by someMethod will be.

Access checks that could result in runtime exceptions would be a major source of bugs in Java application. The language restriction under discussion here avoids this nasty problem.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
9

You can't restrict access because you have already allowed more access in the super class. e.g.

SuperClass sc = new SubClass();
sc.foo(); // is package local, not private.

The access of sc is determined by the type of the reference sc not what it references because it is impossible for the compiler to know in all cases what type the object is at run time. For this to be a safe assumption the sub-class must honour the contract given by the parent or it fails to be a valid subclass. This is no different to the parent saying a method is implemented but the sub class saying it is not (or not accessible)

You could work around this by saying you can only access the sub-class method via the parent, not directly. The problem with this is you don't know when a parent might add a method and when you make a method private you do this because you want it to be private, and not accessible another way.

BTW You can still access a private method via reflection which has the side effect that it cause all sort of problems for the JVM. e.g. it has to keep private methods even though it might determine there is no way it can be called normally.

In short, you want code which means what it says, and not have a split personality. It is either package local or it is private not something sort of in between but not really either. This is not such a problem the other way. i.e. if the sub class is public. It just means the sub-class can be used in more places than the parent, just like it can implement more methods.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
8

If this were allowed, there would be a backdoor through which you could call methods that should not be accessible.

Lets say that this is allowed

class Super {  
    public void method() {  
        System.out.println("Super");  
    }  
}


class Sub extends Super {  
    // This is not allowed, but suppose it was allowed  
    protected void method() {  
        System.out.println("Sub");  
    }  
}

// In another class, in another package:  
Super obj = new Sub();  
obj.method();

obj.method would be possible, because method() is public in class Super. But it should not be allowed, because obj is really referring to an instance of Sub, and in that class, the method is protected!

To restrict a call to a method in class Sub that should not be accessible from the outside, this restriction is put.

Vikas V
  • 3,176
  • 2
  • 37
  • 60
1

Constricting the access modifier of a super class method is an invalid override because it's breaking the super-class contract and invalidates the substitution principle i.e. a sub-class object IS-A super-class object as well.

public void doSomething(SuperClass sc) {
  sc.publicMethodInSuperClass();
}

doSomething(new SubClass()); // would break

If this was allowed, the above client code would break because your SubClass doesn't have that method public.

Reference:
Liskov substitution principle

Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
0

Apart from the obvious problems with using such a constructions(as pointed out by Peter Lawrey in his answer), read about the theory behind it as well: LSP, which means you must be able to substitute the main type with its subclass.

Zavior
  • 6,412
  • 2
  • 29
  • 38
0

I think the short answer is that the compiler writers have set the rules to work this way. LSP has nothing to do with the problem at hand.

The only reason I can think of to have this restriction is that when a subclass derives from an interface, as a client programmer, you expect to be able to call all the accessible methods of the interface from a reference to the derived class.

Just suppose that you could write the code that the OP has shown. If you have a reference to the derived class you should be able to call any public members of the derived class (though there are none in this case). However, pass the reference as a parameter to a method which takes a reference to the base class and the method will expect to call any public or package method, which is foo. This is the LSP that other contributors are looking for!

C++ example:

class Superclass{
public:
virtual void foo(){ cout << "Superclass.foo" << endl; }
};

class Subclass: public Superclass{
virtual void foo(){ cout << "Subclass.foo" << endl; }
};

int main(){
Superclass s1;
s1.foo() // Prints Superclass.foo

Subclass s2;
// s2.foo();  // Error, would not compile

Superclass& s1a=s2; // Reference to Superclass 'pointing' to Subclass

s1a.foo(); // Compiles OK, Prints Subclass.foo()
}
quamrana
  • 37,849
  • 12
  • 53
  • 71
  • *"The only reason I can think of to have this restriction is that when a subclass derives from an interface, as a client programmer, you expect to be able to call all the accessible methods of the interface from a reference to the derived class."* - That's basically LSP. Because, if the client programmer cannot do that, then the subclass cannot be substituted for the parent class! – Stephen C Oct 16 '17 at 23:46
  • No its not. LSP refers to the client's perspective where they only know about the base class and expect the base class to work a certain way. When you supply a derived class to the client because your language allows it, then the derived class should behave the same way, such that the client function still works. – quamrana Oct 17 '17 at 07:57
  • I think you are construing the LSP too narrowly. The Liskov & Wing definition is this: *"Subtype Requirement: Let ϕ ( x ) be a property provable about objects x of type T. Then ϕ ( y ) should be true for objects y of type S where S is a subtype of T."*. In this case, ϕ is having an accessible method `foo`. This is a property of `SuperClass` that clients depend on, and therefore also needs to be a property of `SubClass` ... or else an instance of the latter is not substitutable for an instance of the former. See https://en.wikipedia.org/wiki/Liskov_substitution_principle – Stephen C Oct 17 '17 at 10:54
0

In dynamic method dispatch, call to overridden method is resolved at runtime rather than compile time. It based on the object being referred to at the time of the call...

Now suppose weaker access privilege was allowed and we write the following statement in your code some other class:

Superclass ref=new Subclass();
ref.foo()

Now during runtime, when java comes across the statement ref.foo(), it will have to call foo() of Subclass...but foo() method of subclass is declared as private in your code and private cant be called outside its own class..so now there is a conflict and it would result in runtime exception...

Matt Bryant
  • 4,841
  • 4
  • 31
  • 46
mihir S
  • 617
  • 3
  • 8
  • 23
0

private:

applies on field, method, inner class level
subclass level, we cannot inherit private methods, as scope bounded to within the class

default:

applies on class, field, method level
subclass level, we can increase the scope to protected, public
                 we cannot decrease the scope to private as it is breaking implementation inter-changeable rule;

protected:

applies on field, method level
subclass level, we can increase the scope to public
                  we cannot decrease the scope to default or private as it is breaking implementation inter-changeable rule;

public:

applies on class, field, method level
subclass level, 
           we cannot decrease the scope in sub-class level to
           protected, default or private as it is breaking implementation inter-changeable rule;
Ram Kowsu
  • 711
  • 2
  • 10
  • 30
-1

Because you alter the order of access privileges , so you got error

correct order is:==> private to default to protected to public

*in your case you goes default ==> private whenever you goes in wrong order ,it result is error

Right order of access privilege is--

`class SuperClass{
          void foo(){
    System.out.print("SuperClass");
     }
   class SubClass extends{

  //--default to default--//
         void foo(){
  System.out.print("SubClass");
   }
 //--default to protected--//

  protected void foo(){
  System.out.print("SubClass");

 //--default to public //

 public void foo(){
 System.out.print("SubClass");
 }` 

    **you make sure to preserve correct order while overriding **