12 泛型程序设计

从Java发布1.0版本以来,变化最大的部分就是泛型。泛型机制编写的代码要比杂乱地使用Object变量,然后进行强制类型转换的代码具有更好的安全性和可读性。泛型对集合类尤其有用。

12.1 为什么要使用泛型程序设计

泛型程序设计意味着代码可以被不同类型的对象所重用。

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

类型参数的魅力在于,使得程序有更好的安全性和可读性。

12.2 定义简单泛型类

public class Pair<T> {
 private T first;
 private T second;

 public Pair() {first = null, second = null};
 public Pair(T first, T second) {this.first = first, this.second = second};

 public T getFirst() {return first};
 public T getSecond() {return second};
 
 public void setFirst( T newValue) {first = newValue};
 public void setSecond(T newValue) {second = newValue};
}

泛型类可看作普通类的工厂。

12.3 泛型方法

可以定义一个带类型参数的简单方法

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

注意,类型变量放在修饰符后面,返回类型前面。

泛型方法可以定义在普通类中,也可以定义在泛型类当中。

当调用一个泛型方法时,在方法名前的方括号里面放入具体的类型:

String middle = ArrayAlg.<String>getMiddle("John", "Ann", "Sue");

大多数情况下,可以省略<String>类型参数。

12.4 类型变量的限定

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

选择extends的原因是更接近子类的概念。

一个类型变量或通配符可以有多个限定,例如

T extends Comparable & Serializable
public class PairTest1
{
   public static void main(String[] args)
   {
      String[] words = { "Mary", "had", "a", "little", "lamb" };
      Pair<String> mm = ArrayAlg.minmax(words);
      System.out.println("min = " + mm.getFirst());
      System.out.println("max = " + mm.getSecond());
   }
}

class ArrayAlg
{
   /**
    * Gets the minimum and maximum of an array of strings.
    * @param a an array of strings
    * @return a pair with the min and max value, or null if a is null or empty
    */
   public static Pair<String> minmax(String[] a)
   {
      if (a == null || a.length == 0) return null;
      String min = a[0];
      String max = a[0];
      for (int i = 1; i < a.length; i++)
      {
         if (min.compareTo(a[i]) > 0) min = a[i];
         if (max.compareTo(a[i]) < 0) max = a[i];
      }
      return new Pair<>(min, max);
   }
}
View Code
import java.util.*;


public class PairTest2
{
   public static void main(String[] args)
   {
      GregorianCalendar[] birthdays = 
         { 
            new GregorianCalendar(1906, Calendar.DECEMBER, 9), // G. Hopper
            new GregorianCalendar(1815, Calendar.DECEMBER, 10), // A. Lovelace
            new GregorianCalendar(1903, Calendar.DECEMBER, 3), // J. von Neumann
            new GregorianCalendar(1910, Calendar.JUNE, 22), // K. Zuse
         };
      Pair<GregorianCalendar> mm = ArrayAlg.minmax(birthdays);
      System.out.println("min = " + mm.getFirst().getTime());
      System.out.println("max = " + mm.getSecond().getTime());
   }
}

class ArrayAlg
{
  
   public static <T extends Comparable> Pair<T> minmax(T[] a) 
   {
      if (a == null || a.length == 0) return null;
      T min = a[0];
      T max = a[0];
      for (int i = 1; i < a.length; i++)
      {
         if (min.compareTo(a[i]) > 0) min = a[i];
         if (max.compareTo(a[i]) < 0) max = a[i];
      }
      return new Pair<>(min, max);
   }
}
View Code

12.5 泛型代码和虚拟机

虚拟机中没有泛型类型对象——所有对象都属于普通类。

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。原始类型的名字就是删去了类型参数之后的泛型类型名。擦除类型变量,并替换为限定类型。无限定类型的变量用Object。

原始类型用第一个限定的类型变量来替换。

12.5.1 翻译泛型表达式

如果擦除返回类型,编译器插入强制类型转换。

12.5.2 翻译泛型方法

桥方法被编译器合成用来保持多态。

12.5.3 调用遗留代码

12.6 约束与局限性

12.6.1 不能用基本类型实例化类型参数

12.6.2 运行时类型查询只适用于原始类型

无论何时使用instanceof或涉及泛型类型的强制类型转换表达式都会看到一个编译器警告。

同样的道理,getClass方法总是返回原始类型。

12.6.3 不能创建参数化类型的数组

12.6.4 Varargs警告

12.6.5 不能实例化类型变量

不能使用像new T(..)或T.class这样的表达式中的类型变量。

不能构造一个泛型数组。

12.6.6 泛型类的静态上下文中类型变量无效

不能在静态域或方法中引用类型变量。

12.6.7 不能捕获或者抛出泛型类的实例

12.6.8 注意擦除之后的冲突

12.7 泛型类型的继承规则

无论S和T有什么联系,通常,Pair<S>和Pair<T>没有什么联系。

这一限制看起来非常严格,但对于类型安全来说非常必要。

12.8 通配符类型

Pair<? extends Employee>

表示任何泛型Pair类型,它的类型参数是Employee的子类。

Pair<Manager>和Pair<Employee>都是它的子类型。

12.8.1 通配符的超类型限定

直观的讲,带有超类型限定的通配符可以向泛型类型写入,带有子类型限定的通配符可以从泛型类型读取。

12.8.2 无限定通配符

12.8.3 通配符捕获

12.9 反射和泛型

现在,Class类是泛型的。例如,String.class其实是Class<String>类的一个实例,唯一的一个。

import java.lang.reflect.*;
import java.util.*;

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("[]");
      }
   }
}
View Code

12.9.1 使用Class<T>进行类型匹配

12.9.2 虚拟机中的泛型类型信息

 

posted @ 2020-05-05 10:42  ayor  阅读(155)  评论(0)    收藏  举报