3

I am trying to understand this recursive interface definition in apache thrift source code

 public interface TBase<T extends TBase<?, ?>, F extends TFieldIdEnum> extends Comparable<T>, Serializable {

From my understanding TBase is a interface containing type parameter T and F.

T has the constraint that it also have to be extending TBase that has type parameter containing any type.

What I am confused about is what is the terminating TBase

Say I have

public class TBaseImpl<A, B> implements TBase<A, B>

A has to be a TBase

so there must be another class implement A

public class TBaseImplA<C, D> implements TBase<C, D>

but C have to be a TBase

so there must be another class implement C

This goes on forever.

So my question is

  1. What is terminating condition for TBase
  2. What is the benefit of this recursive definition?

Can someone point me a direction?

Thanks

1 Answer 1

4

so there must be another class implement A

That is not necessarily true. With this type of recursive bounds, there are 2 possible ways to satisfy the constraint when creating a subtype.

  1. Declare your own type parameter with the same or more restrictive bounds. This places the burden of choosing the type parameter on the user of this class.
public class TBaseImpl<A extends TBase<A, B>, B extends TFieldIdEnum> implements TBase<A, B>

or more likely

public class TBaseImpl<A extends TBaseImpl<A, B>, B extends TFieldIdEnum> implements TBase<A, B>
  1. Pass the same class as what you're defining to satisfy the original bound.
public class TBaseImpl<B extends TFieldIdEnum> implements TBase<TBaseImpl, B>

A benefit of this pattern is being able to restrict the parameter of a method that is meant to take in another instance of the same class, e.g.:

public void example(T other)

This is (in Java) the Curiously Repeating Template Pattern.

Normally an implementing/overriding method must match the parameter types and order of parameters exactly. But this pattern allows you to narrow the type by narrowing the type parameter. E.g. such a method in TBaseImpl in this case would only take a TBaseImpl and not the broader T or TBase. In such a class there is a relationship between the class and itself.

Another benefit is method chaining, in which a method returns this to allow

obj.method1().method2().method3()

In this way, chaining methods can be declared to return T, so that e.g. a TBase<TBaseImpl> variable can call these methods, each returning a TBaseImpl on which another method can be called.

T method1();  // in TBase

@Override
TBaseImpl method1(); // in TBaseImpl

Incidentally, if you're trying to declare a type variable that is a subtype of an enum, that's not necessary because an enum is final and cannot be extended. It would be simpler to remove F in the interface and have implementing classes use the enum directly.

Sign up to request clarification or add additional context in comments.

1 Comment

"A benefit of this pattern is being able to restrict the parameter of a method that is meant to take in another instance of the same class, e.g.: public void example(T other)" But it doesn't restrict T to be an instance of the same class

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.