简体   繁体   中英

Recursive interface type parameter in Java

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

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, eg:

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. Eg 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 eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM