`
leonzhx
  • 浏览: 793336 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
阅读更多

1.  In Java SE 7 and beyond, you can omit the generic type in the constructor:

ArrayList<String> files = new ArrayList<>();

The omitted type is inferred from the type of the variable.

 

2.  Generic programming falls into three skill levels. At a basic level, you just use generic classes without thinking how and why they work. Then you need to learn enough about Java generics to solve problems systematically rather than through random tinkering. Finally, of course, you may want to implement your own generic classes and methods.

 

3.  As a rule of thumb, only code that traditionally involved lots of casts from very general types (such as Object or the Comparable interface) will benefit from using type parameters.

 

4.  A generic class is a class with one or more type variables. You can define a generic class by introducing a type variable T, enclosed in angle brackets < >, after the class name.

 

5.  It is common practice to use uppercase letters for type variables, and to keep them short. The Java library uses the variable E for the element type of a collection, K and V for key and value types of a table, and T (and the neighboring letters U and S, if necessary) for “any type at all”.

 

6.  You instantiate the generic type by substituting types for the type variables, such as Pair<String>. In other words, the generic class acts as a factory for ordinary classes.

 

7.  

class ArrayAlg
{
   public static <T> T getMiddle(T... a)
   {
      return a[a.length / 2];
   }
}

It is a generic method, as you can see from the angle brackets and the type variable. Note that the type variables are inserted after the modifiers (public static, in our case) and before the return type. You can define generic methods both inside ordinary classes and inside generic classes.

 

8.  When you call a generic method, you can place the actual types, enclosed in angle brackets, before the method name:

String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");

 

9.  The compiler can infer the actual types of the type parameter by matching return type and method parameters.

 

10.  In almost all cases, type inference for generic methods works smoothly. Occasionally, the compiler gets it wrong, and you’ll need to decipher an error report:

double middle = ArrayAlg.getMiddle(3.14, 1729, 0);

The error message complains, in cryptic terms that vary from one compiler version to another, that there are two ways of interpreting this code, both equally valid. In a nutshell, the compiler autoboxed the parameters into a Double and two Integer objects, and then it tried to find a common supertype of these classes. It actually found two: Number and the Comparable interface, which is itself a generic type.

 

11.  The notation <T extends BoundingType> expresses that T should be a subtype of the bounding type. Both T and the bounding type can be either a class or an interface.

 

12.  A type variable or wildcard can have multiple bounds. The bounding types are separated by ampersands (&) because commas are used to separate type variables: T extends Comparable & Serializable. You can have as many interface supertypes as you like, but at most one of the bounds can be a class. If you have a class as a bound, it must be the first one in the bounds list.

 

13.  The virtual machine does not have objects of generic types—all objects belong to ordinary classes. Whenever you define a generic type, a corresponding raw type is automatically provided. The name of the raw type is simply the name of the generic type, with the type parameters removed. The type variables are erased and replaced by their bounding types (or Object for variables without bounds.)

 

14.  The raw type replaces type variables with the first bound, or Object if no bounds are given. The compiler inserts casts to other bound types when necessary. For efficiency, you should therefore put tagging interfaces (that is, interfaces without methods) at the end of the bounds list.

 

15.  When you program a call to a generic method, the compiler inserts casts when the return type has been erased. Casts are also inserted when you access a generic public field.

 

16.  Erasure of methods brings up a couple of complexities. Consider this example:

class DateInterval extends Pair<Date>
{
   public void setSecond(Date second)
   {
      if (second.compareTo(getFirst()) >= 0)
         super.setSecond(second);
   }
   . . .
}

we’ll want to override the methods to ensure that the second value is never smaller than the first. This class is erased to

class DateInterval extends Pair // after erasure
{
   public void setSecond(Date second) { . . . }
   . . .
}


There is another setSecond method, inherited from Pair, namely,

public void setSecond(Object second)

This is clearly a different method because it has a parameter of a different type—Object instead of Date. So it’s overload instead of override. To fix this problem, the compiler generates a bridge method in the DateInterval class:

public void setSecond(Object second) { setSecond((Date) second); }

 

17.  Bridge methods are not limited to generic types. It is legal for a method to specify a more restrictive return type when overriding another method. For example:

public class Employee implements Cloneable
{
   public Employee clone() throws CloneNotSupportedException { ... }
}

The Object.clone and Employee.clone methods are said to have covariant return types.
Actually, the Employee class has two clone methods:

Employee clone() // defined above
Object clone() // synthesized bridge method, overrides Object.clone

The synthesized bridge method calls the newly defined method.

 

18.  The key facts about translation of Java generics:
    a)  There are no generics in the virtual machine, only ordinary classes and methods.
    b)  All type parameters are replaced by their bounds.
    c)  Bridge methods are synthesized to preserve polymorphism.
    d)  Casts are inserted as necessary to preserve type safety.

 

19.  You can get an object of a raw type from a legacy class. You can assign it to a variable whose type uses generics and Vice Versa. But you will get a warning.

 

20.  You cannot substitute a primitive type for a type parameter. Because after erasure the type variable is replaced with Object (or the first bound), you can’t use it to store double value.

 

21.  Runtime type inquiry only works with raw types. You will get a compiler error (with instanceof) or warning (with casts) when you try to inquire whether an object belongs to a generic type. In the same spirit, the getClass method always returns the raw type.

 

22.  You cannot instantiate arrays of parameterized types. Only the creation of these arrays is outlawed. You can declare a variable of type Pair<String>[]. But you can’t initialize it with a new Pair<String>[10]. (Because when it’s erased to Pair[], you cannot avoid adding Pair<Integer> into it by array store check)

 

23.  You can declare arrays of wildcard types and then cast them:

Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];
The result is not safe, the array can store instance of Pair with any actual types.

 

Commented By Sean : so parameterized type array is of no use ?

 

24.  Consider the method that takes varargs:

public static <T> void addAll(Collection<T> coll, T... ts)
{
   for (T t : ts) coll.add(t);
}
and the following call:
Collection<Pair<String>> table = ...;
Pair<String> pair1 = ...;
Pair<String> pair2 = ...;
addAll(table, pair1, pair2);
In order to call this method, the Java virtual machine must make an array of Pair<String>, but you only get a warning, not an error. As of Java SE 7, you can annotate the addAll method itself with @SafeVarargs. Then this method can be called with generic types. You can use this annotation for any methods that merely read the elements of the parameter array.

 

 

25.  You can use the @SafeVarargs annotation to defeat the restriction against generic array creation, using this method:

@SafeVarargs static <E> E[] array(E... array) { return array; }
Now you can call
Pair<String>[] table = array(pair1, pair2);
This seems convenient, but there is a hidden danger.

 

 

26.  You cannot use type variables in expression such as new T(...), new T[...], or T.class.

 

27.  As a workaround, you can construct generic objects through reflection, by calling the Class.newInstance method. You must design the API so that you are handed a Class object, like this:

 

public static <T> Pair<T> makePair(Class<T> cl)
{
   try { return new Pair<>(cl.newInstance(), cl.newInstance()) }
   catch (Exception ex) { return null; }
}

 

 

28.  The Class class is itself generic. For example, String.class is an instance (indeed, the sole instance) of Class<String>.

 

29.  You cannot construct a generic array:

public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // ERROR
Type erasure would cause this method to always construct an array Comparable[2].(Thus, this array can not only store actual type instance but also all the subtype of Comaprable.) Instead you can use reflection and call Array.newInstance:
public static <T extends Comparable> T[] minmax(T... a)
{
   T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);
   . . .
}

 

30.  The toArray method of the ArrayList class is not so lucky. It needs to produce a T[] array, but it doesn’t have the component type. Therefore, there are two variants:

 

Object[] toArray()
T[] toArray(T[] result)
The second method receives an array parameter. If the array is large enough, it is used. Otherwise, a new array of sufficient size is created, using the component type of result.

 

 

31.  You cannot reference type variables in static fields or methods. Because static members can only have one copy for each class. After type erasure, Pair<T> is erased to Pair and cannot hold several copy of its static fields for different actual type of T.

 

32.  You can neither throw nor catch objects of a generic class. In fact, it is not even legal for a generic class to extend Throwable.

 

33.  You cannot use a type variable in a catch clause:

public static <T extends Throwable> void doWork(Class<T> t)
{
   try
   {
      do work
   }
   catch (T e) // ERROR--can't catch type variable
   {
      Logger.global.info(...)
   }
}
However, it is OK to use type variables in exception specifications. The following method is legal:
public static <T extends Throwable> void doWork(T t) throws T // OK
{
   try
   {
      do work
   }
   catch (Throwable realCause)
   {
      t.initCause(realCause);
      throw t;
   }
}
 

 

 

34.  You have to catch all checked exceptions inside the run method of a thread and wrap them into unchecked exceptions—the run method is declared to throw no checked exceptions. But in the following codes, we don’t wrap. We simply throw the exception, tricking the compiler into believing that it is not a checked exception:

public abstract class Block
{
   public abstract void body() throws Exception;

   public Thread toThread()
   {
      return new Thread()
         {
            public void run()
            {
               try
               {
                  body();
               }
               catch (Throwable t)
               {
                  Block.<RuntimeException>throwAs(t);
               }
            }
         };
   }

   @SuppressWarnings("unchecked")
   public static <T extends Throwable> void throwAs(Throwable e) throws T
   {
        throw (T) e;
   }
}
Commented by Sean: in this example, if you just cast the Throwable to RuntimeException explicitly , you still can bypass the restriction.

 

 

35.  The restriction that a class or type variable may not at the same time be a subtype of two interface types which are different parameterizations of the same interface is imposed:

class Calendar implements Comparable<Calendar> { . . . }

class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . } // ERROR
 
GregorianCalendar would then implement both Comparable<Calendar> and Comparable<GregorianCalendar>, which are different parameterizations of the same interface. The reason is far more subtle. There would be a conflict with the synthesized bridge methods. A class that implements Comparable<X> gets a bridge method
public int compareTo(Object other) { return compareTo((X) other); }
You cannot have two such methods for different types X.

 

 

36.  There is no relationship between Pair<S> and Pair<T>, no matter how S and T are related.

 
37.  You can assign a Manager[] array to a variable of type Employee[]:

Manager[] managerBuddies = { ceo, cfo };
Employee[] employeeBuddies = managerBuddies; // OK
But you cannot do this:
ArrayList<Manager> managerBuddies = new ArrayList<Manager>(Arrays.asList(ceo,cfo));
ArrayList<Employee> employeeBuddies = managerBuddies;//ERROR
Because arrays come with special protection. If you try to store a lowly employee into employeeBuddies[0], the virtual machine throws an ArrayStoreException.

 

 

38.  You can always convert a parameterized type to a raw type. A parameterized type is a subtype of its raw type.

 

39.  Generic classes can extend or implement other generic classes. In this regard, they are no different from ordinary classes. For example, the class ArrayList<T> implements the interface List<T>. That means, an ArrayList<Manager> can be converted to a List<Manager>. However, an ArrayList<Manager> is not an ArrayList<Employee> or List<Employee>.
 
 
40.  The wildcard type Pair<? extends Employee> denotes any generic Pair type whose type parameter is a subclass of Employee, such as Pair<Manager>, but not Pair<String>. The type Pair<Manager> is a subtype of Pair<? extends Employee>:
 

 
41.  For subtype bounds wildcard, you cannot call methods that receive a type variable. i.e.:

void setFirst(T a)
If the actual parameter is ? extends Employee, the compiler only knows that it needs some subtype of Employee, but it doesn’t know which type. It refuses to pass any specific type. But for those methods that return a type variable, it’s ok to assign the return value to Employee reference. (Because the actual type should be a subclass of Employee.)

 

 

42.  You can specify a supertype bound: ? super Manager. This wildcard is restricted to all supertypes of Manager. For method:

void setFirst(T a)
The compiler doesn’t know the exact type of the setFirst method and therefore can’t call it with an object of type Employee or Object, but only with type Manager or a subtype such as Executive. (Because instances of any subclass of Manager are the instances of superclass of Manager) There is no guarantee about the type of the returned object for a method that returns a type variable. You can only assign it to an Object.
 
 
43.  Intuitively speaking, wildcards with supertype bounds let you write to a generic object, while wildcards with subtype bounds let you read from a generic object.

 

 

44.  For method:

public static <T extends Comparable<T>> T min(T[] a)

 
it cannot deal with GregorianCalendar, because it’s a subclass of Calendar, and Calendar implements Comparable<Calendar>. Thus, GregorianCalendar implements Comparable<Calendar> but not Comparable<GregorianCalendar>. Supertypes come to the rescue:

public static <T extends Comparable<? super T>> T min(T[] a)

45.  You can even use wildcards with no bounds at all. The difference between a raw type and a wildcard type with no bounds is that the latter cannot invoke its method that receives a type variable.

 

46.  The getEnumConstants method of Class<T> returns null if this class is not an enum class.

 

47.    You can use the reflection API to determine that
    a)  The generic method has a type parameter called T;
    b)  The type parameter has a subtype bound that is itself a generic type;
    c)  The bounding type has a wildcard parameter;
    d)  The wildcard parameter has a supertype bound;
    e)  The generic method has a generic array parameter.
In other words, you can reconstruct everything about generic classes and methods that their implementors declared. However, you won’t know how the type parameters were resolved for specific objects or method calls.

 

48.  In order to express generic type declarations, use the interface Type in the java.lang.reflect package. The interface has the following subtypes:
    a)  The Class class, describing concrete types
    b)  The TypeVariable interface, describing type variables (such as T extends Comparable<? super T>)
    c)  The WildcardType interface, describing wildcards (such as ? super T)
    d)  The ParameterizedType interface, describing generic class or interface types (such as Comparable<? super T>)
    e)  The GenericArrayType interface, describing generic arrays (such as T[])
 
49.  For an exhaustive discussion of everything there is to know about Java generics, turn to Angelika Langer’s excellent list of frequently (and not so frequently) asked questions at http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html.



 

50.  The following program prints out the information for a generic class:

public class GenericReflectionTest
 {
    public static void main(String[] args)
    {
       // read class name from command line args or user input
       String name;
       if (args.length > 0) name = args[0];
       else
       {
          Scanner in = new Scanner(System.in);
          System.out.println("Enter class name (e.g. java.util.Collections): ");
          name = in.next();
       }

       try
       {
          // print generic info for class and public methods
          Class<?> cl = Class.forName(name);
          printClass(cl);
          for (Method m : cl.getDeclaredMethods())
             printMethod(m);
       }
       catch (ClassNotFoundException e)
       {
          e.printStackTrace();
       }
    }

    public static void printClass(Class<?> cl)
    {
       System.out.print(cl);
       printTypes(cl.getTypeParameters(), "<", ", ", ">", true);
       Type sc = cl.getGenericSuperclass();
       if (sc != null)
       {
         System.out.print(" extends ");
         printType(sc, false);
       }
       printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);
       System.out.println();
    }
    public static void printMethod(Method m)
    {
       String name = m.getName();
       System.out.print(Modifier.toString(m.getModifiers()));
       System.out.print(" ");
       printTypes(m.getTypeParameters(), "<", ", ", "> ", true);

       printType(m.getGenericReturnType(), false);
       System.out.print(" ");
       System.out.print(name);
       System.out.print("(");
       printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
       System.out.println(")");
    }

    public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition)
    {
       if (pre.equals(" extends ") && Arrays.equals(types, new Type[] { Object.class })) return;
       if (types.length > 0) System.out.print(pre);
       for (int i = 0; i < types.length; i++)
       {
          if (i > 0) System.out.print(sep);
          printType(types[i], isDefinition);
       }
       if (types.length > 0) System.out.print(suf);
    }

    public static void printType(Type type, boolean isDefinition)
    {
       if (type instanceof Class)
       {
         Class<?> t = (Class<?>) type;
         System.out.print(t.getName());
       }
       else if (type instanceof TypeVariable)
       {
          TypeVariable<?> t = (TypeVariable<?>) type;
          System.out.print(t.getName());
          if (isDefinition)
             printTypes(t.getBounds(), " extends ", " & ", "", false);
       }
       else if (type instanceof WildcardType)
       {
          WildcardType t = (WildcardType) type;
          System.out.print("?");
          printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
          printTypes(t.getLowerBounds(), " super ", " & ", "", false);
       }
       else if (type instanceof ParameterizedType)
       {
          ParameterizedType t = (ParameterizedType) type;
          Type owner = t.getOwnerType();
          if (owner != null)
          {
             printType(owner, false);
             System.out.print(".");
          }
          printType(t.getRawType(), false);
          printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
       }
       else if (type instanceof GenericArrayType)
       {
          GenericArrayType t = (GenericArrayType) type;
          System.out.print("");
          printType(t.getGenericComponentType(), isDefinition);
          System.out.print("[]");
       }
    }
 }

 

  • 大小: 110.2 KB
  • 大小: 163.8 KB
  • 大小: 122.8 KB
  • 大小: 150.5 KB
  • 大小: 121.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics