Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Inherited Fluent method return type to return incident class' type, not parent's

When you employ a fluent approach you might have the following scenario:

public class Foo<T extends a>{

    public Foo<T> someMethod(){
          System.out.println("foo");
          return this;
    }

}


public class Bar<T extends b> extends Foo<T> {

         public Bar<T> barMethod(){

            System.out.println("bar");
            return this;
         }

}


public class a{}
public class b extends a{}

A strength of a fluent interface is that you can chain method invocations, but whilst Bar has inherited someMethod(), the return type is Foo not Bar which would break the following chain:

new Bar<b>().someMethod().barMethod();

One answer could be to add @Overrides for every inherited method, such that Bar has the additional method:

 @Override
 public Bar<T> someMethod(){
          System.out.println("foo");
          return this;
    }

But in large classes, and extended class hierarchies this can prove a mess of redundancy surely?! Is there an appropriate return type to give to fluent methods whereby every class that inherits it will return an object of its specific type (so that we can chain methods without casts)?

I tried:

        public <U extends Foo<T>> U someMethod(){
          System.out.println("foo");
          return this;
    }

To alas, no avail. I am hoping someone knows of a simple and elegant solution to this problem. The project is large and so it needs to be maintainable and extensible if possible.

Thank you to any help you can provide in this scenario.

like image 323
Darius Avatar asked Oct 29 '12 09:10

Darius


2 Answers

You can do it like this, if you don't mind an unchecked cast:

public class Foo<T, F extends Foo<T, F>> {
  public F someMethod() {
    System.out.println("foo");
    return (F) this; // <--- That's the unchecked cast! Tucked safely away.
  }
  public static void main(String[] args) {
    new Bar<String>().someMethod().barMethod();
  }
}

class Bar<T> extends Foo<T, Bar<T>> {
  public Bar<T> barMethod() {
    System.out.println("bar");
    return this;
  }
}

Personally, I disable the unchecked cast warning globally for the whole project.

like image 169
Marko Topolnik Avatar answered Nov 06 '22 17:11

Marko Topolnik


What you can do is define barMethod method abstract so now you can call it on the Foo type.

public abstract class Foo<T extends a> {

    public Foo<T> someMethod() {
        System.out.println("foo");
        return this;
    }

    public abstract Foo<T> barMethod();
}

Now you can easily call

new Bar<b>().someMethod().barMethod();
like image 43
Amit Deshpande Avatar answered Nov 06 '22 16:11

Amit Deshpande