Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics: T extends MyClass vs. T extends MyClass<T>

Tags:

java

generics

Is there a semantic difference between these two declaration or is it only syntactic sugar?

class C<T extends C> vs class C<T extends C<T>>

Background: I recently answered a question on generics using the C<T extends C> approach and a peer provided a similar answer based on C<T extends C<T>>. At the end, both alternatives provided the same result (in the context of the question asked). I remained curious about the difference between these two constructs.

Is there's a semantic difference? If so, what are the implications and consequences of each approach?

like image 443
maasg Avatar asked Jun 29 '11 15:06

maasg


People also ask

What does the T designation indicate in a generic class?

T is a parameter that is associated with the generic type that is being defined in this class. To use the generic container, you must assign the type of the container by specifying it at instantiation using the angle bracket notation.

Can we extend generic class in Java?

Yes, you can use any valid Java identifier for type parameters.

What is the difference between T and E in Java generics?

Well there's no difference between the first two - they're just using different names for the type parameter ( E or T ). The third isn't a valid declaration - ? is used as a wildcard which is used when providing a type argument, e.g. List<?>

Which of these is the correct way to declare a bounded type?

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number .


2 Answers

Sure - often these "self types" are used to constrain subtypes to return exactly their own type. Consider something like the following:

public interface Operation {
    // This bit isn't very relevant
    int operate(int a, int b);
}

public abstract class AbstractOperation<T extends AbstractOperation<T>> {
    // Lets assume we might need to copy operations for some reason
    public T copy() {
        // Some clever logic that you don't want to copy and paste everywhere
    }
}

Cool - we have a parent class with a useful operator which can be specific to subclasses. For instance, if we create an AddOperation, what can its generic parameters be? Because of the "recursive" generic definition, this can only be AddOperation giving us:

public class AddOperation extends AbstractOperation<AddOperation> {
    // Methods etc.
}

And hence the copy() method is guaranteed to return an AddOperation. Now lets imagine we're silly, or malicious, or creative, or whatever, and try to define this class:

public class SubtractOperation extends AbstractOperation<AddOperation> {
    // Methods etc.

    // Because of the generic parameters, copy() will return an AddOperation
}

This will be rejected by the compiler because the generic type isn't within its bounds. This is quite important - it means that in the parent class, even though we don't know what the concrete type is (and it might even be a class that didn't exist at compile time), the copy() method will return an instance of that same subclass.

If you simply went with C<T extends C>, then this weird definition of SubtractOperation would be legal, and you lose the guarantees about what T is in that case - hence the subtract operation can copy itself into an add operation.

This isn't so much about protecting your class hierarchy from malicious subclasses, it's more that it gives the compiler stronger guarantees about the types involved. If you're calling copy from another class altogther on an arbitrary Operation, one of your formations guarantees that the result will be of the same class, while the other will require casting (and might not be a correct cast, as with the SubtractOperation above).

Something like this for example:

// This prelude is just to show that you don't even need to know the specific
// subclass for the type-safety argument to be relevant
Set<? extends AbstractOperation> operations = ...;
for (AbstractOperation<?> op : operations) {
    duplicate(op);
}

private <T extends AbstractOperation<T>> Collection<T> duplicate(T operation) {
    T opCopy = operation.copy();
    Collection<T> coll = new HashSet<T>();
    coll.add(operation);
    coll.add(opCopy);

    // Yeah OK, it's ignored after this, but the point was about type-safety! :)
    return coll; 
}

The assignment on the first line of duplicate to T wouldn't be type-safe with the weaker of the two bounds you proposed, so the code wouldn't compile. Even if you define all of the subclasses sensibly.

like image 118
Andrzej Doyle Avatar answered Sep 28 '22 21:09

Andrzej Doyle


Let's say you have a class called Animal

class Animal<T extends Animal>

If T is Dog, it would mean that Dog should be a subclass of Animal< Dog>, Animal< Cat>, Animal< Donkey> anything.

class Animal<T extends Animal<T>>

In this case if T is Dog, it has to be a subclass of Animal< Dog> and nothing else, i.e. you must have a class

class Dog extends Animal<Dog>

you can't have

class Dog extends Animal<Cat>

And even if you have that, you can't use Dog as a type parameter for Animal and you must have

class Cat extends Animal<Cat>

In many cases there is a lot of difference between the two, especially if you have some constraints applied.

like image 33
aps Avatar answered Sep 28 '22 21:09

aps