When Overriding a Method, Why How to Increase Access But Not Decrease It

When overriding a method, why can I increase access but not decrease it?

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.

Why can't we change access modifier while overriding methods in C#?

Changing the access modifier of a method in a derived type is pointless that's why it's not allowed:

Case 1: Override with a more restrictive access

This case is obviously not allowed due to the following situation:

class Base
{
public virtual void A() {}
}

class Derived: Base
{
protected override void A()
}

Now we could say:

List<Base> list;
list.Add(new Derived());
list[0].A() //Runtime access exception

Case 2: Overriding with a less restrictive access modifier

What is the point? Hide the method and you are done.
Obviously if someone calls through the base type they will not have access to the new method defined in the derived type but that is consistent with how the author of the base type wanted things to be so you have no "right" to change that. If you want the specifics of the derived class call from the derived class, in which case the new method works perfectly fine.

EDIT: Expanding case 2

What I am trying to say in case 2, is that you already have the means to change accessibility of any method (virtual or not) if you want to change accessibility.

Consider the following code:

public class Base
{
protected virtual string WhoAmI()
{
return "Base";
}
}

public class Derived : Base
{
public new virtual string WhoAmI()
{
return "Derived";
}
}

public class AnotherDerived : Derived
{
public override string WhoAmI()
{
return "AnotherDerived";
}
}

With the new keyword you have effectively created a new virtual method for your Derived class with the same name and signature. Take note that it is ALLOWED to declare a new method virtual, so any class deriving from Derived will be allowed to override it.

What is not allowed is to have someone do the following:

 Base newBaseObject = new Derived();
newBaseObject.WhoAmI() //WhoAmI is not accessible.

But this fact has nothing to do with being able to override WhoAmI() or not. Whatever the case this situation can never be because Base does not declare a public WhoAmI().

So in a theoretical C# where Derived.WhoAmI() could override Base.WhoAmI() there is no practical benefits in doing so because you will never be able to call the virtual method from the base class anyways, so the new option already meets your requirements.

I hope this makes it clearer.

when interface method override always use access modifier public why?

The reason you must use public in your class, is because your method is implicitly public from the interface. The Java tutorial Defining an Interface says, in part,

All abstract, default, and static methods in an interface are implicitly public, so you can omit the public modifier.

Change the access modifier of an overridden method in Java?

Java doesn't let you make the access modifier more restrictive, because that would violate the rule that a subclass instance should be useable in place of a superclass instance. But when it comes to making the access less restrictive... well, perhaps the superclass was written by a different person, and they didn't anticipate the way you want to use their class.

The programs people write and the situations which arise when programming are so varied, that it's better for language designers not to "second-guess" what programmers might want to do with their language. If there is no good reason why a programmer should not be able to make access specifiers less restrictive in a subclass (for example), then it's better to leave that decision to the programmer. They know the specifics of their individual situation, but the language designer does not. So I think this was a good call by the designers of Java.

What happens when you mark overridden methods with different access descriptors?

It's fine to make it more accessible. From the Java Language Specification section 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, or a compile-time error occurs. In more detail:

  • 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.

Why does java allow for overriding methods with subclass return types but not subclass parameters?

When parameters allow doing this, they are called contravariant parameters. When you do it with return types, they are called covariant return types. Java supports covariant return types but not contravariant parameters.

That would change the method signature and it will no longer be an override. Return types are not part of the method signature but the type and the number of parameters of the method are part of the signature, hence this would interfere with overloading.

Moreover, if Java allowed you to do that, that would cause unexpected behavior in some cases and break runtime polymorphism with virtual methods because you are narrowing down what the method could accept.

Imagine a scenario where some code or an API only exposes the base class to you. You call someMethod() to receive a person:

public Person someMethod()
{
Child child = new Child();
return child;
}

This is how it would look like at the calling site:

Person receivedPerson = someMethod();
Person myPerson = new Person();
Person result = receivedPerson.getPerson(myPerson); // This will fail

Here, the caller does not know that someMethod() has actually returned a Child instead of a Person. I would think the Person class has a method called getPerson() accepting an object of type Person and therefore, I can call receivedPerson.getPerson(myPerson). But, as the derived type Child which you don't even know about has changed the parameter type of getPerson() to Child, it will not be able to accept myPerson because you cannot convert an object of the base class to an object of a derived class.

This will never happen with covariant return types because if a method returns a more specific type, say Child instead of Person, it can be easily stored in a Person variable and child will always have all the state and behavior as that of its parent.

Why can't you reduce the visibility of a method in a Java subclass?

Because every instance of the subclass still needs to be a valid instance of the base class (see Liskov substitution principle).

If the subclass suddenly has lost one property of the base class (namely a public method for example) then it would no longer be a valid substitute for the base class.

Can an overriding method have a different access specifier from that in the base class?

It is possible to relax the restriction, but not to make it more restrictive:

public abstract class A {
protected void method();
}

public class B extends A {
@Override
public void method(){ // OK
}
}

public class C extends A {
@Override
private void method(){ // not allowed
}
}

Making the original method private won't work either, since such method isn't visible in subclasses and therefore cannot be overriden.

I would recommend using interfaces to selectively expose or hide the method:

public interface WithMethod {
// other methods
void method();
}

public interface WithoutMethod {
// other methods
// no 'method()'
}

public abstract class A {
protected void method();
}

public class B extends A implements WithMethod {
@Override
public void method(){
//TODO
}
}

public class C extends B implements WithoutMethod {
// no 'method()'
}

... then only work with the instances through the interfaces.



Related Topics



Leave a reply



Submit