Thursday, June 5, 2014

Overriding - let's check how it works

what is method overriding?

Method overriding is a concept based on polymorphism which allows us to create a method with the same signature as in parent class to extend or change its behaviour. Let’s take a look at the definition:
Method overriding - allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. The implementation in the subclass overrides the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class.
Today we will check what is possible to override and what's not. I will also try to explain why it is so.

let's start with something funny

As it's written in the definition, overriding allows subclasses to provide different implementation of method which is already defined in a parent class.
But what will happen if we would like to override a method in the same class?
class Parent {
    boolean overridenMethod() {
        return true;
    }

    boolean overridenMethod() {
        return false;
    }
}

and test:
@Test
public void overridingSelfMethod() {
    Parent obj = new Parent();

    assertTrue(obj.overridenMethod());
}

This may be illogical, but when I run it the test was successful. Somehow the second definition is ignored. If we will change assertion into assertFalse there's no error, just java.lang.AssertionError. But if we change method order it passes once again.
I assume that during compilation the second definition is just ignored.
However, IDE marked Parent class as invalid and suggested changing the name of it.

Ok, now let's move on to something more useful :)

changing accessibility

As far I remember, overriding method can not reduce accessibility of overridden method in Java. If it's true then code below:
class Parent {
    public boolean overriden() {
        return true;
    }
}

class Child extends Parent {
    protected boolean overriden() {
        return false;
    }
}

shouldn't pass my test:
@Test
public void changingVisibility() {
    Child obj = new Child();

    assertFalse(obj.overriden());
}

If you run it, you will see the following error:
java.lang.Error: Unresolved compilation problem: 
    Cannot reduce the visibility of the inherited method from Parent
But opposite is true, overriding a method can increase accessibility of the method. This means that if we change accessibilities in classes above everything will be ok.

declared exceptions

A similar thing happen with exceptions. Overriding this method cannot throw checked exception which is higher in hierarchy than overridden method.
Take a look at this code:
class Parent {
    protected boolean overriden() throws IOException {
        return true;
    }
}

class Child extends Parent {
    public boolean overriden() throws Exception {
        return false;
    }
}

and run the following test:
@Test(expected = Exception.class)
public void throwsException() throws Exception {

    Child obj = new Child();

    obj.overriden();
}

It will result with:
java.lang.Error: Unresolved compilation problem: 
    Exception Exception is not compatible with throws clause in Parent.overriden()
But, as in the case with accessibility, opposite is true. Just try to use instead of Exception class something which is lower in hierarchy than IOException. Error will disappear and test will pass.

what happened?

The reason behind behavior shown in two paragraphs above is polymorphism. Each subclass which extends any other class have to fulfill its contracts (interfaces) and needs to provide at least the same API. There's no problem with adding something more, but there's no possibility of throwing something out.
If there would be a chance to decrease accessibility, it would mean that we make API smaller. Method would no longer be visible by other classes and we could reach a place where this method is needed, but is no longer accessible.
If there would be possibility to throw exception which is higher in hierarchy, there would be a chance to spoil the code by using subclass in places where we handle exceptions defined in parent method's signature. The contract won't be fulfilled.

what about private and static?

Private and static methods are bonded during compile time using static binding in Java and doesn't resolve during runtime. That's why code:
class Parent {
    String getString() {
        return getName() + " " + getStringPart();
    }

    static String getName() {
        return Parent.class.toString();
    }

    private String getStringPart() {
        return "part from parent";
    }
}

class Child extends Parent {

    static String getName() {
        return Child.class.toString();
    }

    private String getStringPart() {
        return "part from child";
    }
}  

won't pass the test:
@Test
public void overridingDoesntWork() {
    Child obj = new Child();

    assertSame("class com.smalaca.overriding.Child part from child", obj.getString());
}

There will be no error. getString() method will just use different methods than we expected and will return: "class com.smalaca.overriding.Parent part from parent".
I believe that was obvious for you. Private methods are visible only within the scope of the class and neither inheritance nor any other manipulation will give us access to it.
However, it's good to remember that both private and static method can be hidden if you declare another method with same signature in subclass.

... and final methods

Ok, I will skip test for final method. Those methods cannot be overridden. Why? Because that's what final word is designed for - it's a sign saying that "you cannot override this".

... and the final word

At the end, I have one tip for you.
Whenever you will think that overriding will may be a good solution for you, rethink your decision once again. Or even twice. Why? If it's your code and you have to override method it means that either the method will do something differently than parent or it will do something more. Yeah, I know, it's obvious :)
However, in both cases, it can be a sign of bad design. In the first case, maybe it's worth extracting body of the method to different classes and use Strategy pattern? In the second case, maybe you didn't notice something before and maybe it would be good to consider using Template Method pattern? Of course, those two suggested solutions aren't good for any cases, but show that there are different ways to solve your problem. And probably are better than overriding. Whenever you see that method is overriden you never know whether whole implementation was changed or its part or maybe something more was added. You never know what the motives were. Not without looking at the code.

I'm not saying that you cannot use overriding, but it's good to take a deep breath, consider pros and cons and be fully aware of the decision you're making.


No comments:

Post a Comment