Overriding chained method

Let's have a class A

with a method useful for chaining:

class A {
  A foo() {
    // do stuff
    return this;
  }
}

      

Now you can do a.foo().foo().foo()

(think: builder pattern). Let class B extend it:

class B extends A {
}

B b;

      

The call now b.foo()

returns a valid type B

, but declared A

. Therefore, I cannot write:

B other = b.foo();

      

I would need to write:

B other = (B) b.foo();

      

Or override foo()

in B:

class B extends A {
  @Override
  B foo() {
    super.foo();
    return this;
  }
}

      

Is there a good way to do this?

+3


source to share


4 answers


I did it by implementing an additional generic method as(type)

for the superclass. It helps to make casting for a fluent interface more enjoyable.

class A {
    public A foo() {
        return this;
    }
    @SuppressWarnings("unchecked")
    public <T extends A> T as(Class<T> clazz) {
        return (T) this;
    }
}

class B extends A  {}

      



So you can write a.foo().as(B.class).methodOfB(..)

. And you don't need to override foo () in all subclasses.

+1


source


Sorry, but you haven't already provided the best way:

B other = (B) b.foo();

      

This will be the solution for any developer who uses A

and B

.

Rewriting is for you so other developers can just write B other = b.foo();

But there is no other way for the compiler to know that

  • B

    is kind A

  • B

    compatible, so you won't be using any information when instantiating A

    , but put it inB



The latter is the reason you need to explicitly specify. Example:

 int myInt = 2;
 short myShort = (short) myInt; // correct

      

although this works, the compiler requires you to be explicit because in many cases (when myInt

large) you lose precision / information when you click on short

.

If this works:

short myShort = myInt;   // wrong

      

Then the compiler will make its own guess. But only the developer can know that if myInt

it ever matters, more than short

that can persist.

+1


source


You can declare class A

to have the general type of your implementation class, or you foo()

can return this dynamic type:

class A<T extends A<T>> {
    @SuppressWarnings("unchecked")
    T foo() {
        // do stuff
        return (T) this;
    }
}

class B extends A<B> {
    B bar() {
        // do other stuff
        return this;
    }
}

      

After that, the following is valid:

B b = new B();
b.foo().bar();

      

+1


source


I think a good way to do this is to keep the reference as a type A

, as this is the least specific class that has the functionality you need:

A other = b.foo();

      

You should only use type B if B contains some required functionality:

B another = (B)A;
another.somethingElse();

      

This is the same logic that does:

List<String> myList = new ArrayList<>();

      

more desirable than:

ArrayList<String> myList = new ArrayList<>();

      

0


source







All Articles