Reputation: 35351
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.
Upvotes: 18
Views: 4728
Reputation: 1462
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).
Upvotes: 3
Reputation: 65859
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();
}
}
Upvotes: 15
Reputation: 85789
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);
}
}
Upvotes: 13
Reputation: 4157
You can just cast it.
ChainChild child = new ChainChild();
((ChainChild) child.foo()).bar();
You could try:
public ? extends Chain foo() {
return this;
}
But I'm not sure that will help, even if it compiles.
Upvotes: 0