简体   繁体   中英

Java Recursive generic template: what does this mean by … S extends Writer<E>> extends Entity<E,S>

Can some one explain the below, rather complex recursive generic template usage?

public abstract class Data<E extends Data<E, S>,
                           S extends Writer<E>> extends Entity<E,S>

What should we keep in mind while using recursive generics, like above. And how will be the relation and rules between these types, here E & S ?

If any, please provide some resource/links/books about this type of generic usage. I know one book talking about this, Effective Java, 2nd ed by Joshua Bloch (Item 27)

Lets begin with the easiest

S extends Writer<E>

Any class of type S must be a writer for the class E

extends Entity<E,S>

Just inheritance here, The Data class extends the Entity class.

E extends Data<E, S>

Any class used for E must itself inherit from the Data class and inherits/implements the generic methods of Data using its own type and a writer compatible with itself.

The relation between E & S should be something like the following:

//E = Example, S = ExampleWriter
public class ExampleWriter implements Writer<Example>{
//...
}
public class Example extends Data<Example,ExampleWriter>{
//...
}

To keep in mind: with generics providing a Writer<SomeChildOfExample> or a Writer<SomeParentOfExample> might or might not create compiler errors, this depends on the generic methods defined in both generic types.

Data has two parameters, E which must ultimately be an instance of itself, and S which must be able to Writer an instance of itself (more specifically, the same kind of instance of itself specified by E ). Finally, Data<E,S> also qualifies as/inherits capabilities from Entity parametrized by the same E and S (ie, Entity is of Data<E,S> and Writer<E> ).

A concrete implementation might look something like

NumericalData extends Data<NumericalData, NumWriter> where NumWriter implements/extends Writer<NumericalData> and NumericalData also qualifies as an Entity<NumericalData, NumWriter> .

EDIT:

Why do something like this? One might want to define generic methods in the abstract class that rely on an argument/return meeting the criteria Data<E,S> , but also want to be able to return/work with the more explicit type. For example, in Data<E,S> , there might be

E doSomething(E toThis) { toThis.aDataClassMethod(); return toThis; }

The class can make the first call, because it knows E is a Data<E,S> , and return the more specific type because it knows toThis is an E .

To be honest, recursive generics are typically the road to too clever. They can be useful, but many times they're just "neat" and one tries to bend the problem around something clever rather than vice versa.

I agree with Carl that recursive types tend to be "clever" at the expense of usability. However there are a LOT of cases where the Java rtl should have employed this idiom to enforce strict type safety and to avoid the barrel of monkeys we have as a class library.

For example, even Object should probably be an abstract recursive type at least to enforce strict rules for equality:

public abstract class Object<T extends Object<T>> {
  ...
  public boolean equals( o :T ) {
     ...
  }
}

No more instanceof checks in your equals() implementations and, more importantly, better compile-time checking for equals() calls.

That said, perhaps a more suitable and less complicated feature would be a "Self" type...

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