`

Java Generics FAQs - Type Parameters (转)

    博客分类:
  • Java
阅读更多

Type Parameters

Fundamentals

What is a type parameter?

A place holder for a type argument.
Generic types have one or more type parameters. 

Example of a parameterized type: 

interface Comparable<E>  { 
  int compareTo(E other);
}
The identifier E is a type parameter. Each type parameter is replaced by a type argument when an instantiation of the generic type, such as Comparable<Object> or Comparable<? extends Number> , is used.
LINK TO THIS TypeParameters.FAQ001
REFERENCES How is a generic type defined?
What is a bounded type parameter?
Where is a type parameter visible (or invisible)?

What is a bounded type parameter?

A type parameter with one or more bounds.  The bounds restrict the set of types that can be used as type arguments and give access to the methods defined by the bounds.
When you declare a type parameter T and use it in the implementation of a generic type or method, the type parameter T still denotes an unknown type.  The compiler knows that T is a place holder for a type, but it does not know anything about the type.   This is okay in some implementations, but insufficient in others. 

Example (of a generic type without bounds): 

public class Hashtable<Key,Data> {
  ... 
  private static class Entry<Key,Data> {
    private Key key; 
    private Data value;
    private int hash;
    private Entry<Key,Data> next;
    ...
  }
  private Entry<Key,Data>[] table; 
  ... 
  public Data get(Key key) {
    int hash = key.hashCode() ;
    for (Entry<Key,Data> e = table[hash & hashMask]; e != null; e = e.next) {
      if ((e.hash == hash) && e. key.equals(key) ) {
        return e.value;
      }
    } 
    return null;
  }
}
The implementation of class Hashtable invokes the methods hashCode and equals on the unknown Key type.  Since hashCode and equals are methods defined in class Object and available for all reference types, not much need to be known about the unknown Key type.  This changes substantially, when we look into the implementation of sorted sequence. 

Example (of a generic type, so far without bounds): 

public interface Comparable<T> {
  public int compareTo(T arg);
}
public class TreeMap<Key,Data>{
  private static class Entry<K,V> {
     K key;
     V value;
     Entry<K,V> left;
     Entry<K,V> right;
     Entry<K,V> parent;
  }
  private transient Entry<Key,Data> root;
  ...
  private Entry<Key,Data> getEntry(Key key) {
     Entry<Key,Data> p = root;
     Key k = key;
     while (p != null) {
       int cmp = k. compareTo(p.key) ; // error
       if (cmp == 0)
         return p;
       else if (cmp < 0)
         p = p.left;
       else 
         p = p.right; 
     }
     return null;
  }
  public boolean containsKey(Key key) {
     return getEntry(key) != null;
  }
  ...
}
The implementation of class TreeMap invokes the method compareTo on the unknown Key type.  Since compareTo is not defined for arbitrary types the compiler refuses to invoke the compareTo method on the unknown type Key because it does not know whether the key type has a compareTo method. 

In order to allow the invocation of the compareTo method we must tell the compiler that the unknown Key type has a compareTo method.  We can do so by saying that the Key type implements the Comparable<Key> interface.  We can say so by declaring the type parameter Key as a bounded parameter. 

Example (of the same generic type, this time with bounds): 

public interface Comparable<T> {
  public int compareTo(T arg);
}
public class TreeMap<Key extends Comparable<Key> ,Data>{
  private static class Entry<K,V> {
     K key;
     V value;
     Entry<K,V> left = null;
     Entry<K,V> right = null;
     Entry<K,V> parent;
  }
  private transient Entry<Key,Data> root = null;
  ...
  private Entry<Key,Data> getEntry(Key key) {
     Entry<Key,Data> p = root;
     Key k = key;
     while (p != null) {
       int cmp = k. compareTo(p.key)
       if (cmp == 0)
         return p;
       else if (cmp < 0)
         p = p.left;
       else 
         p = p.right; 
     }
     return null;
  }
  public boolean containsKey(Key key) {
     return getEntry(key) != null;
  }
  ...
}
In the example above, the type parameter Key has the bound Comparable<Key> .  Specification of a bound has two effects: 
  • It gives access to the methods that the bound specifies .  In the example, the bound Comparable<Key> gives access to the compareTo method that we want to invoke in the implementation of our TreeMap class.
  • Only types "within bounds" can be used for instantiation of the generic type.   In the example, a parameterized type such as TreeMap<Number,String> would be rejected, because the type Number is not a subtype of Comparable<Number> .  A parameterized type like TreeMap<String,String> would be accepted, because the type String is within bounds, i.e. is a subtype of Comparable<String> .


Note that the suggested bound Comparable<Key> in this example is not the best conceivable solution.  A better bound, that is more relaxed and allows a larger set of type arguments, would be Comparable<? super Key> .  A more detailed discussion can be found in a separate FAQ entry (click here ).

LINK TO THIS TypeParameters.FAQ002
REFERENCES When would I use a wildcard parameterized with a lower bound?
What is a type parameter bound?
Which types are permitted as type parameter  bounds?
Can I use different instantiations of a same generic type as bounds of a type parameteer?
Does a bound that is a class type give access to all its public members?
Can I use a type parameter as part of its own bounds or in the declaration of other type parameters?

Type Parameter Bounds

What is a type parameter bound?

A reference type that is used to further describe a type parameter.  It restricts the set of types that can be used as type arguments and gives access to the non-static methods that it defines.
A type parameter can be unbounded.  In this case any reference type can be used as type argument to replace the unbounded type parameter in an instantiation of a generic type. 

Alternatively can have one or several bounds.  In this case the type argument that replaces the bounded type parameter in an instantiation of a generic type must be a subtype of all bounds. 

The syntax for specification of type parameter bounds is: 

<TypeParameter extends Class & Interface 1 & ... & Interface N >
A list of bounds consists of one class and/or several interfaces. 

Example (of type parameters with several bounds): 

class Pair<A extends Comparable<A> & Cloneable
           B extends Comparable<B> & Cloneable
  implements Comparable<Pair<A,B>>, Cloneable { ... }
This is a generic class with two type arguments A and B , both of which have two bounds.
LINK TO THIS TypeParameters.FAQ101
REFERENCES What is the difference between a wildcard bound and a type parameter bound?
Which types are permitted as type parameter  bounds?
Can I use different instantiations of a same generic type as bounds of a type parameteer?

Which types are permitted as type parameter  bounds?

All classes, interfaces and enum types including parameterized types, but no primitive types and no array types.
All classes, interfaces, and enum types can be used as type parameter bound, including nested and inner types.  Neither primitive types nor array types be used as type parameter bound. 

Examples (of type parameter bounds): 

class X0 <T extends int > { ... }      // error
class X1 <T extends Object[] > { ... } // error
class X2 <T extends Number > { ... }
class X3 <T extends String > { ... }
class X4 <T extends Runnable > { ... }
class X5 <T extends Thread.State > { ... }
class X6 <T extends List > { ... }
class X7 <T extends List<String> > { ... }
class X8 <T extends List<? extends Number> > { ... }
class X9 <T extends Comparable<? super Number> > { ... }
class X10<T extends Map.Entry<?,?> > { ... }
The code sample shows that primitive types such as int and array types such as Object[] are not permitted as type parameter bound. 

Class types, such as Number or String , and interface types, such as Runnable , are permitted as type parameter bound. 

Enum types, such as Thread.State are also permitted as type parameter  bound. Thread.State is an example of a nested type used as type parameter bound.  Non-static inner types are also permitted. 

Raw types are permitted as type parameter bound; List is an example. 

Parameterized types are permitted as type parameter bound, including concrete parameterized types such as List<String> ,  bounded wildcard parameterized types such as List<? extends Number> and Comparable<? super Long> , and unbounded wildcard parameterized types such as Map.Entry<?,?> .  A bound that is a wildcard parameterized type allows as type argument all types that belong to the type family that the wildcard denotes.  The wildcard parameterized type bound gives only restricted access to fields and methods; the restrictions depend on the kind of wildcard. 

Example (of wildcard parameterized type as type parameter bound): 

class X< T extends List<? extends Number> > {
  public void someMethod(T t) {
    t.add(new Long(0L));    // error
    Number n = t.remove(0);
  }
}
class Test {
  public static void main(String[] args) {
     X<ArrayList< Long >>   x1 = new X<ArrayList<Long>>(); 
     X<ArrayList< String >> x2 = new X<ArrayList<String>>(); // error
  }
}
Reference variables of type T (the type parameter) are treated like reference variables of a wildcard type (the type parameter  bound).  In our example the consequence is that the compiler rejects invocation of methods that take an argument of the "unknown" type that the type parameter stands for, such as List.add , because the bound is a wildcard parameterized type with an upper bound. 
At the same time the bound List<? extends Number> determines the types that can be used as type arguments. The compiler accepts all type arguments that belong to the type family List<? extends Number> , that is, all subtypes of List with a type argument that is a subtype of Number
 

Note, that even types that do not have subtypes, such as final classes and enum types, can be used as upper bound.  In this case there is only one type that can be used as type argument, namely the type parameter bound itself. Basically, the parameterization is pointless then. 

Example (of nonsensical parameterization): 

class Box< T extends String > {
  private T theObject;
  public Box( T t) { theObject = t; }
  ...
}
class Test {
  public static void main(String[] args) {
    Box< String > box1("Jack");
    Box< Long >   box2(100L);    // error
  }
}
The compiler rejects all type arguments except String as "not being within bounds". The type parameter T is not needed and the Box class would better be defined as a non-parameterized class.
LINK TO THIS TypeParameters.FAQ102
REFERENCES What is a type parameter bound?
Can I use a type parameter as a type parameter bound?
Can I use different instantiations of a same generic type as bounds of a type parameter?
Can I use a type parameter as part of its own bounds or in the declaration of other type parameters?
How do unbounded wildcard instantiations of a generic type relate to other instantiations of the same generic type?
How do wildcard instantiations with an upper bound relate to other instantiations of the same generic type?
How do wildcard instantiations with a lower bound relate to other instantiations of the same generic type?
Which methods and fields are accessible/inaccessible through a reference variable of a wildcard parameterized type?

Can I use a type parameter as a type parameter bound?

Yes.
A type parameter can be used as the bound of another type parameter. 

Example (of a type parameter used as a type parameter bound): 

class Triple <T> {
  private T fst, snd, trd;
  public < U extends T , V extends T , W extends T > Triple(U arg1, V arg2, W arg3) {
    fst = arg1;
    snd = arg2;
    trd = arg3;
  }
}
In this example the type parameter T of the parameterized class is used as bound of the type parameters U V and W of a parameterized instance method of that class. 

Further opportunities for using type parameters as bounds of other type parameters include situations where a nested type is defined inside a generic type or a local class is defined inside a generic method.  It is even permitted to use a type parameter as bound of another type parameter in the same type parameter section.

LINK TO THIS TypeParameters.FAQ102A
REFERENCES Can I use a type parameter as part of its own bounds or in the declaration of other type parameters?
Which types are permitted as type parameter bounds?
Where is a type parameter visible (or invisible)?
Can I use different instantiations of a same generic type as bounds of a type parameteer?
What is the difference between a wildcard bound and a type parameter bound?

Can I use different instantiations of a same generic type as bounds of a type parameter?
 

No, at most one instantiation of the same generic type can appear in the list of bounds of a type parameter.
Example (of illegal use of two instantiations of the same generic type as bounds of a type parameter): 
class ObjectStore<T extends Comparable<T> & Comparable<String> > { // error 
  private Set<T> theObjects = new TreeSet<T> ();
  ...
  public boolean equals(ObjectStore<String> other) {
    if (theObjects.size() != other.size()) return false;
    Iterator<T> iterThis = theObjects.iterator();
    Iterator<String> iterOther = other.theObjects.iterator();
    while (iterThis.hasNext() && iterOther.hasNext()) {
      T t = iterThis.next();
      String string = iterOther.next();
      if ( t.compareTo(string) != 0)  return false;
    }
    return true;
  }
}


error: java.lang.Comparable cannot be inherited with different arguments: <T> and <java.lang.String>
    class ObjectStore<T extends Comparable<T> & Comparable<String>>  {
                      ^
In the example the type parameter T is required to be Comparable<T> , that is, comparable to its own type.  This is needed for storing objects of type T in a TreeSet<T> .  At the same time the type parameter T is required to be Comparable<String> , because we want to invoke the type parameter's compareTo(String) method. Remember, type parameter bounds are needed to give the compiler access to the type parameters non-static methods.  In this (admittedly contrived) example, we need to specify two instantiations of the Comparable interface as bound of the type parameter, but the compiler rejects it. 

The reason for this restriction is that there is no type that is a subtype of two different instantiations of the Comparable interface and could serve as a type argument.  It is prohibited that a type implements or extends two different instantiations of the same interface. This is because the bridge method generation process cannot handle this situation.  Details are discussed in a separate FAQ entry (click here ).  If no class can ever implement both instantiations of Comparable , there is no point to a bounds list that requires it.  The class in our example would not be instantiable because no type can ever be within bounds, except perhaps class String

In practice, you will need to work around this restriction. Sadly, there might be situations in  which there is no workaround at all.

LINK TO THIS TypeParameters.FAQ103
REFERENCES Can a class implement different instantiations of the same generic interface?
What is type erasure?
What is a bridge method?
How does type erasure work when a type parameter has several bounds?
How can work around the restriction that a type parameter cannot have different instantiations of a same generic type as its bounds?

How can I work around the restriction that a type parameter cannot have different instantiations of a same generic type as its bounds?
 

Usually there is no satisfactory workaround.
Let us use the example from the previous question for our search of a workaround. 

Example (of illegal use of two instantiations of the same generic type as bounds of a type parameter): 

class ObjectStore<T extends Comparable<T> & Comparable<String> > { // error
  pri vate Set<T> theObjects = new TreeSet<T> ();
  ...
  public boolean equals(ObjectStore<String> other) {
    if (theObjects.size() != other.size()) return false;
    Iterator<T> iterThis = theObjects.iterator();
    Iterator<String> iterOther = other.theObjects.iterator();
    while (iterThis.hasNext() && iterOther.hasNext()) {
      T t = iterThis.next();
      String string = iterOther.next();
      if ( t.compareTo(string) != 0)  return false;
    }
    return true;
  }
}
In the example the type parameter T is required to be Comparable<T> , because objects of type T are stored in a TreeSet<T> .  At the same time the type parameter T is required to be Comparable<String> , because we invoke the type parameter's compareTo(String) method. The compiler rejects the attempt of specifying two instantiations of the Comparable interface as bound of the type parameter. 

One workaround for the example above could be the following: we could  drop the requirement that the parameter T must be Comparable<T> , because the corresponding compareTo(T) method is not invoked in the implementation of the generic class itself, but in the operations of the Treeset<T> .  By dropping the requirement we would risk that a type argument is supplied that is not Comparable<T> and will cause ClassCastException s when operations of the TreeSet<T> are invoked.  Clearly not a desirable solution, but perhaps a viable one for this particular example. 

However, this might not be a solution if the class uses the type parameter in a slightly different way. For instance, if both compareTo methods were called in the implementation of the generic class, then we could not drop any of the bounds. 

Example (another class with illegal use of two instantiations of the same generic type as bounds of a type parameter): 

class SomeClass<T extends Comparable<T> & Comparable<String> > { // error 
  ... 
  private void method(T t1, T t2) {
     ... t1.compareTo(t2) ...
     ... t1.compareTo("string") ...
  }
}
If the methods of the bounds are invoked in the class implementation, then dropping one of the conflicting bounds does not solve the problem.  One could consider use of an additional interface, such as a CombinedComparable interface that combines the two required interfaces into one interface. 

Example (conceivable work-around; does not work): 

interface CombinedComparable<T> {
  int compareTo(T other);
  int compareTo(String other);
}
class SomeClass<T extends CombinedComparable<T>
  ... 
  private void m(T t1, T t2) {
     ... t1.compareTo(t2) ...
     ... t1.compareTo("string") ...
  }
  public boolean equals( SomeClass<String> other) {  // error 
    ...
  }
 }
However, this is not really a viable solution, because it excludes class String as a type argument. String is a class that is comparable to itself and to String , but it is does not implement a CombinedComparable interface. Hence type String is not within bounds. Another conceivable alternative is definition of one new interface per instantiation needed, such as a parameterized SelfComparable and a non-parameterized StringComparable interface.  Again, this excludes class String as a potential type argument.  If it acceptable that class String is excluded as a potential type argument then the definition of additional interfaces might be a viable workaround. 

But there remain some situations, in which additional interfaces do not help.  For instance, if the type parameter is used as type argument of another parameterized method, then it must itself be within the bounds of that other type parameter. 

Example (another class with illegal use of two instantiations of the same generic type as bounds of a type parameter): 

class AnUnrelatedClass {
  public static <T extends Comparable<String> > void f(T t) { ... }
}
class AnotherUnrelatedClass {
  public static <T extends Comparable<T> > void g(T t) { ... }
}
class SomeClass<T extends Comparable<T> & Comparable<String> > { // error 
  ... 
  private void h(T t) {
     AnUnrelatedClass.f(t);
     AnotherUnrelatedClass.g(t);
  }
}
No solution sketched out above would address this situation appropriately. If we required that the type parameter be CombinedComparable , it would not be within the bounds of at least one of the two invoked methods. Note, that the CombinedComparable interface can be a subinterface of only one of the two instantiations of Comparable , but not both. 

Example (conceivable work-around; does not work): 

interface CombinedComparable<T> extends Comparable<String> {
  int compareTo(T other);
}
class ObjectStore<T extends CombinedComparable<T>
  ... 
  private void h(T t) {
     AnUnrelatedClass.f(t); 
     AnotherUnrelatedClass.g(t); // error
  }
}
The same happens when we require that the type parameter be SelfComparable and StringComparable . Even if both were subinterfaces of the respective instantiation of Comparable , there cannot be a class that implements both, because that class would indirectly implement the two instantiations of Comparable

Ultimately the realization is that, depending on the circumstances, there might not be a work around at all.

LINK TO THIS TypeParameters.FAQ104
REFERENCES Can I use different instantiations of a same generic type as bounds of a type parameter?
Can a class implement different instantiations of the same parameterized interface?

Does a bound that is a class type give access to all its public members?

Yes, except any constructors.
A bound that is a class gives access to all its public members, that is, public fields, methods, and nested type. Only constructors are not made accessible, because there is no guarantee that a subclass of the bound has the same constructors as the bound. 

Example (of a class used as bound of a type parameter): 

public class SuperClass {

    // static members
    public enum EnumType {THIS, THAT}
    public static Object staticField;
    public static void staticMethod() { ... }

    // non-static members
    public class InnerClass { ... }
    public Object nonStaticField;
    public void nonStaticMethod() { ... }

    // constructors
    public SuperClass() { ... }

    // private members
    private Object privateField; 

    ...
}

public final class SomeClass<T extends SuperClass > {
    private T object;
    public SomeClass(T t) { object = t; } 

    public String toString() {
        return
         "static nested type    : "+T.EnumType.class+"\n" 
        +"static field          : "+T.staticField+"\n"
        +"static method         : "+T.staticMethod()+"\n"
        +"non-static nested type: "+T.InnerClass.class+"\n"
        +"non-static field      : "+object.nonStaticField+"\n"
        +"non-static method     : "+object.nonStaticMethod()+"\n"
        +"constructor           : "+(new T())+"\n"                    // error
        +"private member        : "+object.privateField+"\n"          // error 
        ;
    }
}

The bound SuperClass gives access to its nested types, static fields and methods and non-static fields and methods.  Only the constructor is not accessible.  This is because constructors are not inherited. Every subclass defines its own constructors and need not support its superclass's constructors.  Hence there is no guarantee that a subclass of SuperClass will have the same constructor as its superclass. 

Although a superclass bound gives access to types, fields and methods of the type parameter, only the non-static methods are dynamically dispatched. In the unlikely case that a subclass redefines types, fields and static methods of its superclass, these redefinitions would not be accessible through the superclass bound. 

Example (of a subclass of the bound used for instantiation): 

public final class SubClass extends SuperClass {
    // static members
    public enum Type {FIX, FOXI} 
    public static Object staticField;
    public static Object staticMethod() { ... }

    // non-static members 
    public class Inner { ... }
    public Object nonStaticField;
    public Object nonStaticMethod() { ... }

    // constructors 
    public SubClass(Object o) { ... }
    public SubClass(String s) { ... }

    ...
}



SomeClass< SubClass > ref = new SomeClass<SubClass>(new SubClass("xxx"));
System.out.println(ref);


prints:
static nested type    : SuperClass .EnumType
static field          : SuperClass .staticField
static method         : SuperClass .staticMethod  => SuperClass.staticField
non-static nested type: SuperClass .InnerClass
non-static field      : SuperClass .nonStaticField
non-static method     : SubClass .nonStaticMethod => SubClass.nonStaticField
Calling the nonStaticMethod results in invocation of the subclass's overriding version of the nonStaticMethod . In contrast, the subclass's redefinitions of types, fields and static methods are not accessible through the bounded parameter.  This is nothing unusual.  First, it is poor programming style to redefine in a subclass any of the superclass's nested types, fields and static methods.  Only non-static methods are overridden.  Second, the kind of hiding that we observe in the example above also happens when a subclass object is used through a superclass reference variable.
LINK TO THIS TypeParameters.FAQ105
REFERENCES

How do I decrypt "Enum<E extends Enum<E>>"?

As a type that can only be instantiation for its subtypes, and those subtypes will inherit some useful methods, some of which take subtype arguments (or otherwise depend on the subtype).
The context in which " Enum<E extends Enum<E>> " appears is the declaration of the Enum class in package java.lang
public abstract class Enum<E extends Enum<E>> {
  ...
}
The type Enum is the common base class of all enumeration types.  In Java an enumeration type such as Color is translated into a class Color that extends Enum<Color> . The purpose of the superclass Enum is to provide functionality that is common to all enumeration types. 

Here is a sketch of class Enum

public abstract class Enum< E extends Enum<E>> implements Comparable< E >, Serializable {
  private final String name;
  public  final String name() { ... }

  private final int ordinal;
  public  final int ordinal() { ... }

  protected Enum(String name, int ordinal) { ... }

  public String           toString() { ... }
  public final boolean    equals(Object other) { ... }
  public final int        hashCode() { ... }
  protected final Object  clone() throws CloneNotSupportedException { ... }
  public final int        compareTo( E o) { ... }

  public final Class< E > getDeclaringClass() { ... }
  public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { ... }
}

The surprising feature in the declaration  " Enum<E extends Enum<E>> " is the fact that the newly defined class Enum and its newly defined type parameter E appear in the bound of that same type parameter.  It means that the Enum type must be instantiated for one of its subtypes.  In order to understand why this makes sense, consider that every enum type is translated into a subtype of Enum

Here is the contrived enum type Color

enum Color {RED, BLUE, GREEN}
The compiler translates it into the following class: 
public final class Color extends Enum<Color> {
  public static final Color[] values() { return (Color[])$VALUES.clone(); }
  public static Color valueOf(String name) { ... }

  private Color(String s, int i) { super(s, i); }

  public static final Color RED;
  public static final Color BLUE;
  public static final Color GREEN;

  private static final Color $VALUES[];

  static {
    RED = new Color("RED", 0);
    BLUE = new Color("BLUE", 1);
    GREEN = new Color("GREEN", 2);
    $VALUES = (new Color[] { RED, BLUE, GREEN });
  }
}

The inheritance has the effect that the Color type inherits all the methods implemented in Enum<Color> .  Among them is the compareTo method.  The Color.compareTo method should probably take a Color as an argument.  In order to make this happen class Enum is generic and the Enum.compareTo method takes Enum 's type parameter E as an argument.  As a result, type Color derived from Enum<Color> inherits a compareTo method that takes a Color as and argument, exactly as it should. 
 

If we dissect the declaration " Enum<E extends Enum<E>> " we can see that this pattern has several aspects. 

First, there is the fact that the type parameter bound is the type itself: " Enum <E extends Enum <E>> ". It makes sure that only subtypes of type Enum are permitted as type arguments. (Theoretically, type Enum could be instantiated on itself, like in Enum<Enum> , but this is certainly not intended and it is hard to imagine a situation in which such an instantiation would be useful.)

Second, there is the fact that the type parameter bound is the parameterized type Enum <E> ,  which uses the type parameter E as the type argument of the bound. This declaration makes sure that the inheritance relationship between a subtype and an instantiation of Enum is of the form " X extends Enum<X> ". A subtype such as " X extends Enum<Y> " cannot be declared because the type argument Y would not be within bounds; only subtypes of Enum<X> are within bounds.

Third, there is the fact that Enum is generic in the first place.  It means that some of the methods of class Enum take an argument or return a value of an unknown type (or otherwise depend on an unknown type). As we already know, this unknown type will later be a subtype X of Enum<X> .  Hence, in the parameterized type Enum<X> , these methods involve the subtype X , and they are inherited into the subtype X .  The compareTo method is an example of such a method; it is inherited from the superclass into each subclass and has a subclass specific signature in each case. 

To sum it up, the declaration " Enum<E extends Enum<E>> " can be decyphered as: Enum is a generic type that can only be instantiated for its subtypes, and those subtypes will inherit some useful methods, some of which take subtype specific arguments (or otherwise depend on the subtype).

LINK TO THIS TypeParameters.FAQ106
REFERENCES How is a generic type defined?
What is a bounded type parameter?
Where is a type parameter visible (or invisible)?

http://www.angelikalanger.com/Images/bar-small
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics