I'm trying to implement the fluet interface pattern. So basicly the idea is to have chaining methods, but i'am mixing it with the strategy pattern, so i can have multiple implementations of "Foo"
I have this abstract class:
public abstract class Foo<T extends BarData> {
T data;
public Foo(T data) {
this.data = data;
}
public Foo<T> fooMethod {
// do stuff with data
return this;
}
}
This is another implementation of Foo that provides another method:
public class Bar extends Foo<BarData> {
public Bar(BarData data) {
super(data);
}
public Bar barMethod() {
// do more stuff with data
return this;
}
}
Fluent Interface:
public interface FluentInterface<T extends BarData, S extends Foo<T>> {
S initialize();
}
Basic implementation:
public class BasicFluentInterfaceImplementation implements FluentApi<BarData, Bar> {
@Override
Bar initialize() {
// prepares data
return new Bar(data);
}
}
Using all together:
public class Main {
public static void main(String[] str) {
FluentInterface api = new BasicFluentInterfaceImplementation();
var firstReturn = api.initialize(); // Here it returns Bar
var secondReturn = firstReturn.fooMethod(); // As Bar extends Foo i can call the fooMethod()
var thirdReturn = secondReturn.barMethod(); // Here is the problem, i can't call the barMethod, because fooMethod already returned an intance of Foo
}
}
How can i make Foo return a instance of Bar? Considering that Bar can be anything that extends Foo.
CodePudding user response:
I have mostly bad news; there is no real solution to this problem. Fluent interfaces are indeed nice, but this scenario you describe in your question suggests that it's not worth doing here, as it's mostly confusing now, that you can't chain a call to barMethod
from fooMethod
. Better to just revert back to void
-returning methods for such a hierarchy.
There IS a hack available, but, it's.. messy.
public abstract class Foo<T extends BarData, Z extends Foo<T>> {
T data;
public Foo(T data) {
this.data = data;
}
public Z fooMethod() {
// do stuff with data
return self();
}
@SuppressWarnings("all")
protected Z self() {
return (Z) this;
}
}
Z here is meant to represent 'self', as in, whatever type the object actually is, even in the definition of a supertype. In other words, the return type of self()
is supposed to be Bar
for an instance of Bar, even though it is defined in Foo
.
You unfortunately have to 'mess' with the Z stuff when defining subtypes: class Bar extends Foo<BarData, Bar> {}
. The second parameter there is always your own type, it 'makes it work'.
Is this hack worth it to you? Up to you. Don't be too hasty assuming it is.