tl;dr
Trying to implement a hierarchal fluent interface such that I can combine nodes child classes while also the class standalone, but getting type parameter is not within its bound errors.
Details
I'm trying to achieve a solution so that I can create something such that I can do something like:
farm
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
while also being able to do:
Human human = new Human()
.saysHello()
I've gotten close using various strategies but haven't been able to gain the flexibility described.
My current attempt uses the following classes:
abstract class Base<T extends Base<T>>{
private T parent;
Base(){
}
Base( T parent ){
this.parent = parent;
}
public T done() throws NullPointerException{
if ( parent != null ){
return (T) parent;
}
throw new NullPointerException();
}
}
class Farm<T extends Base<T>> extends Base{
private Animal<Farm<T>> animal;
private Human<Farm<T>> human;
public Farm(){
super();
this.animal = new Animal( this );
this.human = new Human( this );
}
public Animal<Farm> animal(){
return this.animal;
}
public Human<Farm<T>> human(){
return this.human;
}
}
class Animal <T extends Base<T>> extends Base{
private Cat<Animal<T>> cat;
private Dog<Animal<T>> dog;
public Animal(){
super();
init();
}
public Animal( T parent ){
super( parent );
init();
}
private void init(){
this.cat = new Cat(this);
this.dog = new Dog(this);
}
public Cat<Animal<T>> cat(){
return cat;
}
public Dog<Animal<T>> dog(){
return dog;
}
}
class Human<T extends Base<T>> extends Base{
public Human<T> saysHello(){
System.out.println("human says hi");
return this;
}
}
class Cat <T extends Base<T>> extends Base{
private Human<Cat> human;
public Cat(){
super();
init();
}
public Cat( T parent ){
super( parent );
init();
}
private void init(){
this.human = new Human();
}
public Cat<T> meow(){
System.out.println("cat says meow");
return this;
}
public Human<Cat<T>> findsHuman(){
return this.human;
}
}
class Dog <T extends Base<T>> extends Base{
private Human<Dog> human;
public Dog(){
super();
init();
}
public Dog( T parent ){
super( parent );
init();
}
private void init(){
this.human = new Human();
}
public Dog<T> bark(){
System.out.println("dog says woof");
return this;
}
public Dog<T> chacesCar(){
System.out.println("cat drinks milk");
return this;
}
public Human<Dog<T>> findsHuman(){
return this.human;
}
}
The errors I'm seeing are commonly:
Animal.java:4: type parameter Animal is not within its bound private Cat cat; Animal.java:5: type parameter Animal is not within its bound private Dog dog;
Applied to all similar references and also pertaining to my example desired case:
cannot find symbol symbol : method dog() location: class Base.dog()
I've tried using the following solutions which seemed to tackle similar problems, but to no avail, so any and all support is welcome.
References
Is there a way to refer to the current type with a type variable?
http://vyazelenko.com/2012/03/02/recursive-generics-to-the-rescue/
The code below seems to work fine and doesn't need any @SuppressWarnings
. The key concept to grasp is that your T
parameter is effectively the class of your object's parent, but T
's parent could be anything. So instead of T extends Base<T>
you want T extends Base<?>
.
The output is:
cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi
...which I believe is correct, although you might want to change your Dog.chacesCar()
method so it doesn't output cat drinks milk
! Also it should be chases
not chaces
.
Hope this helps!
abstract class Base<T extends Base<?>> {
private final T parent;
Base() {
this.parent = null;
}
Base(T parent) {
this.parent = parent;
}
public T done() throws NullPointerException {
if (parent != null) {
return parent;
}
throw new NullPointerException();
}
}
class Farm<T extends Base<?>> extends Base<T> {
private final Animal<Farm<T>> animal;
private final Human<Farm<T>> human;
public Farm() {
super();
this.animal = new Animal<>(this);
this.human = new Human<>(this);
}
public Animal<Farm<T>> animal() {
return this.animal;
}
public Human<Farm<T>> human() {
return this.human;
}
}
class Animal<T extends Base<?>> extends Base<T> {
private Cat<Animal<T>> cat;
private Dog<Animal<T>> dog;
public Animal() {
super();
init();
}
public Animal(T parent) {
super(parent);
init();
}
private void init() {
this.cat = new Cat<>(this);
this.dog = new Dog<>(this);
}
public Cat<Animal<T>> cat() {
return cat;
}
public Dog<Animal<T>> dog() {
return dog;
}
}
class Human<T extends Base<?>> extends Base<T> {
public Human() {
super();
}
public Human(T parent) {
super(parent);
}
public Human<T> saysHello() {
System.out.println("human says hi");
return this;
}
}
class Cat<T extends Base<?>> extends Base<T> {
private Human<Cat<T>> human;
public Cat() {
super();
init();
}
public Cat(T parent) {
super(parent);
init();
}
private void init() {
this.human = new Human<>(this);
}
public Cat<T> meow() {
System.out.println("cat says meow");
return this;
}
public Human<Cat<T>> findsHuman() {
return this.human;
}
}
class Dog<T extends Base<?>> extends Base<T> {
private Human<Dog<T>> human;
public Dog() {
super();
init();
}
public Dog(T parent) {
super(parent);
init();
}
private void init() {
this.human = new Human<>(this);
}
public Dog<T> bark() {
System.out.println("dog says woof");
return this;
}
public Dog<T> chacesCar() {
System.out.println("cat drinks milk");
return this;
}
public Human<Dog<T>> findsHuman() {
return this.human;
}
}
Test code:
public static void main(String[] args) {
Farm<?> farm = new Farm<>();
farm
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
Human human = new Human()
.saysHello();
}
The best thing I came up is the following:
new Animal()
.cat()
.meow()
.findsHuman()
.<Cat>done()
.<Animal>done()
.dog()
.bark()
.findHuman()
.<Dog>done()
.done();
With the following base class:
public abstract class Base<T extends Base<T>>{
private Base<?> backRef;
public Base() {}
public Base(Base<?> backRef) {
this.backRef = backRef;
}
@SuppressWarnings("unchecked")
protected T self() {
return (T)this;
}
@SuppressWarnings("unchecked")
public <U extends Base<U>> U done() {
return (U)backRef;
}
}
If you declare backRef as of Type T then the other classes are not allowed because they are not a subclasses of each other, so you have to specify a different type, but since this type is context dependent (one time its Cat, one time its Dog) I don't see an alternative as to pass a hint.
I found a solution:
new Animal()
.cat()
.meow()
.findsHuman()
.done()
.done()
.dog()
.bark()
.findHuman()
.done()
.done();
public abstract class Base<T extends Base<T,P>, P>{
private P backRef;
public Base() {}
public Base(P backRef) {
this.backRef = backRef;
}
@SuppressWarnings("unchecked")
protected T self() {
return (T)this;
}
public P done() {
return backRef;
}
}
Like someone suggested, we add an additional Type for the parent.
Now the base classes:
public final class Cat extends Base<Cat, Animal>{
public Cat() {}
public Cat(Animal backRef) {
super(backRef);
}
public Cat meow() {
System.out.println("Meeeoooww");
return self();
}
public Human<Cat> findsHuman() {
return new Human<Cat>(this);
}
}
As you can see, Cat clearly specifies which base type it should use. Now for human, which can change the type depending on the context:
public final class Human<P> extends Base<Human<P>, P> {
public Human() {}
public Human(P backRef) {
super(backRef);
}
}
Human specifies an additional generic which the caller (Cat, Dog) specifies in their findHuman() Method.
This is what we did on one our project:
public abstract class Parent<T extends Parent<T>> {
/**
* Get {@code this} casted to its subclass.
*/
@SuppressWarnings("unchecked")
protected final T self() {
return (T) this;
}
public T foo() {
// ... some logic
return self();
}
// ... other parent methods
}
public class Child extends Parent<Child> {
public Child bar() {
// ... some logic
return self();
}
// ... other child methods
}
Allowing child to have its own subclass would be:
public class Child<T extends Child<T>> extends Parent<T> {
public T bar() {
// ... some logic
return self();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With