When overriding a method, why can I increase access but not decrease it?
Why does Java specify that the access specifier for an overriding method can allow more, but not less, access than the overridden method?开发者_Go百科 For example, a protected instance method in the superclass can be made public, but not private, in the subclass.
It's a fundamental principle in OOP: the child class is a fully-fledged instance of the parent class, and must therefore present at least the same interface as the parent class. Making protected/public things less visible would violate this idea; you could make child classes unusable as instances of the parent class.
Imagine these two classes:
public class Animal {
public String getName() { return this.name; }
}
public class Lion extends Animal {
private String getName() { return this.name; }
}
I could write this code:
Animal lion = new Lion();
System.out.println( lion.getName() );
And it would have to be valid, since on Animal the method getName() is public, even tho it was made private on Lion. So it is not possible to make things less visible on subclasses as once you have a superclass reference you would be able to access this stuff.
Because it would be weird:
class A {
public void blah() {}
}
class B extends A {
private void blah() {}
}
B b = new B();
A a = b;
b.blah(); // Can't do it!
a.blah(); // Can do it, even though it's the same object!
Take an example given below
class Person{
public void display(){
//some operation
}
}
class Employee extends Person{
private void display(){
//some operation
}
}
Typical overriding happens in the following case
Person p=new Employee();
Here p
is the object reference with type Person(super class) when we are calling
p.display(). As the access modifier is more restrictive, the object reference p
cannot access child object of type Employee
Late to the party but I would like to add one more concern related to overriding: The overriding method must allow less (or the same level of) throwable exception than the overridden method; even nothing throwable at all.
Liskov substitution principle can explain that too:
interface Actionable {
void action() throws DislocationException;
}
public class Actor implements Actionable {
@Override
public void action() throws DislocationException {
//....
}
}
public class Stuntman implements Actionable {
@Override // this will cause compiler error
public void action() throws DislocationException, DeathException {
//....
}
}
// legacy code to use Actionable
try {
Actionable actor = new Actor(); // this cannot be replaced by a Stuntman,
// or it may break the try/catch block
actor.action();
} catch (DislocationException exc) {
// Do something else
}
Above, the overridden method made a commitment that in the worst case it will throw the DislocationException, no more (a doctor is required at the filming location). So the overriding method must not break that, by adding more DeathException (or an ambulance is a must)
I often call the overriding rule "[can be] more access [level], [but] less exception"
Because a subclass is a specialization of the superclass, or in other words, it's an extension of the superclass.
Imagine for instance the toString method. All Java objects have it because the class Object has it. Imagine you could define a class with the method toString private. You then no longer treat all Objects equally. For instance, you would no longer be able to this safely:
for (Object obj : collection) System.out.println(obj);
Well, in terms of the specific case you mentioned, how exactly would Java handle that? If the subclass made a public/protected method private, then what should the JVM do when that method is invoked on an instance of the subclass? Honor the private and invoke the superclass' implementation? Further, you're breaking the contract specified by the superclass when you suddenly say "no one can access this method, despite what the contract initially said."
To re-word what's already been said, it has to do with how Java is compiled into bytecode which is then interpreted by the JVM. when a child class overrides one of its parents methods, the compiler uses the reference type to determine which of the two methods to use. Then the JVM uses the object type during runtime to determine which method should truly be used.
In the example above; Animal lion = new Lion()
, when lion.getName()
is called, the compiler uses the Animal version of the method and the JVM replaces it/can replace it with the Lion version because it "fits" perfectly. But if Lion were allowed to restrict getName() more than Animal restricted getName(), you could get around the restriction because the compiler would treat it like its unrestricted if a Lion object has an Animal reference.
To solve this, java makes it illegal for the child to make an overridden method more restricted than the method its overriding.
精彩评论