Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OOP in Java: Class inheritance with method chaining

Tags:

java

oop

I have a parent class, which defines a collection of chainer methods (methods that return "this"). I want to define multiple child classes that contain their own chainer methods but that also "override" the parent methods so that an instance of the child class is returned instead of the parent class.

I don't want to have to repeat the same methods in each child class, which is why I have a parent class that contains the methods that all the child classes share. Thanks.

class Chain {
  public Chain foo(String s){
    ...
    return this;
  }
}

class ChainChild extends Chain {
  //I don't want to add a "foo" method to each child class
  /*
  public ChildChain foo(String s){
    ...
    return this;
  }
  */

  public ChainChild bar(boolean b){
    ...
    return this;
  }
}

ChainChild child = new ChainChild();
child.foo().bar(); //compile error: foo() returns a "Chain" object which does not define the bar() method. 
like image 346
Michael Avatar asked Feb 24 '13 17:02

Michael


People also ask

What is method chaining in Java?

Method Chaining is the practice of calling different methods in a single line instead of calling other methods with the same object reference separately. Under this procedure, we have to write the object reference once and then call the methods by separating them with a (dot.).

What do you mean by method chaining?

Method chaining, also known as named parameter idiom, is a common syntax for invoking multiple method calls in object-oriented programming languages. Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results.

What is method chaining Why is it bad?

The drawback to self-referential method chaining is that you communicate that multiple method calls are required to do something, and that each call builds off the last. If this is not true, then method chaining could be communicating the wrong thing to other programmers.

How inheritance is used in OOPs?

Inheritance in OOP = When a class derives from another class. The child class will inherit all the public and protected properties and methods from the parent class. In addition, it can have its own properties and methods. An inherited class is defined by using the extends keyword.


3 Answers

A method in the parent class that returns this will still return a reference to the object of the child class. You will only be able to treat it as an object of the parent class (unless you cast it) but it will actually be of its original type.

You could consider using generics like this:

// This seems a bit too contrived for my liking. Perhaps someone else will have a better idea.
public class Parent<T extends Parent<T>> {
    T foo () {
        return (T) this;
    }
}

public class Child extends Parent<Child> {
    public void bar () {
        Child c = foo();
    }
}
like image 78
OldCurmudgeon Avatar answered Oct 08 '22 00:10

OldCurmudgeon


I've written this sample using generics based on your needs.

class Parent {
    public <T extends Parent> T foo() {
        return (T)this;
    }
}

class Child extends Parent {

}

class AnotherChild extends Parent {

}

public class Test {
    public static void main(String[] args) {

        Parent p = new Child();
        System.out.println(p);
        Child c = p.foo();
        System.out.println(c);
        //throws ClassCastException here since Child is not AnotherChild
        AnotherChild ac = p.foo();
        System.out.println(ac);
    }
}
like image 12
Luiggi Mendoza Avatar answered Oct 07 '22 23:10

Luiggi Mendoza


Here's an improvement on OldCurmudgeon's method:

public class Parent<This extends Parent<This>> {

    @SuppressWarnings("unchecked")
    private final This THIS = (This)this;

    public This foo() {
        //...
        return THIS;
    }

    public This bar() {
        //...
        return THIS;
    }    
}

public class Child extends Parent<Child> {

    // you can override super with covariant return type
    @Override
    public Child bar() {
        super.bar();

        return this;
    }
}


Child child = 
new Child()
.foo()
.bar();

The improvement is making an unchecked cast only once and save it to a constant THIS, which you can reuse as the return value for method chaining.

This removes unchecked casts in every method and makes the code clearer.

You can override Parent's methods by returning Child instead of This (covariant return type).

like image 3
d4n3 Avatar answered Oct 08 '22 00:10

d4n3