Java – Is overriding concrete methods a code smell

javaobject-orientedoverriding

Is it true that overriding concrete methods is a code smell? Because I think if you need to override concrete methods:

public class A{
    public void a(){
    }
}

public class B extends A{
    @Override
    public void a(){
    }
}

it can be rewritten as

public interface A{
    public void a();
}

public class ConcreteA implements A{
    public void a();
}

public class B implements A{
    public void a(){
    }
}

and if B wants to reuse a() in A it can be rewritten as:

public class B implements A{
    public ConcreteA concreteA;
    public void a(){
        concreteA.a();
    }
}

which does not require inheritance to override the method, is that true?

Best Answer

No, it is not a code smell.

  • If a class is not final, it allows to be subclassed.
  • If a method is not final, it allows to be overridden.

It lies within the responsabilities of each class to carefully consider if subclassing is appropriate, and which methods may be overridden.

The class may define itself or any method as final, or may place restrictions (visibility modifiers, available constructors) on how and where it is subclassed.

The usual case for overriding methods is a default implementation in the base class which may be customized or optimized in the subclass (especially in Java 8 with the advent of default methods in interfaces).

class A {
    public String getDescription(Element e) {
        // return default description for element
    }
}

class B extends A {
    public String getDescription(Element e) {
        // return customized description for element
    }
}

An alternative for overriding behaviour is the Strategy Pattern, where the behaviour is abstracted as an interface, and the implementation can be set in the class.

interface DescriptionProvider {
    String getDescription(Element e);
}

class A {
    private DescriptionProvider provider=new DefaultDescriptionProvider();

    public final String getDescription(Element e) {
       return provider.getDescription(e);
    }

    public final void setDescriptionProvider(@NotNull DescriptionProvider provider) {
        this.provider=provider;
    }
}

class B extends A {
    public B() {
        setDescriptionProvider(new CustomDescriptionProvider());
    }
}
Related Topic