Java 泛型编程详解(2)

三、泛型的命名规范
    为了更好地去理解泛型,我们也需要去理解Java泛型的命名规范。为了与java关键字区别开来,java泛型参数只是使用一个大写字母来定义。各种常用泛型参数的意义如下:
E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
S,U,V etc. - 2nd, 3rd, 4th 类型,和T的用法一样
四、泛型的方法与构造函数
    有时候我们并不希望整个类都被泛型化,这时可以只在某个方法上应用泛型。因为构造函数是一种特殊的方法,因此也可以在构造函数上应用泛型。Demo GenMethod演示了如何在方法上应用泛型和调用泛型方法,
public class GenMethod {
 
 public static <T> void fromArrayToCollection(T[] a,Collection<T> c){
  for (T t : a) {
    c.add(t);
  }
 }
 
 public static void main(String[] args) {
  Object[] oa = new Object[100];
  Collection<Object> co = new ArrayList<>();
 
  GenMethod.<Object>fromArrayToCollection(oa, co);
 } 
}
    GenMethod 的代码不多,不过需要注意的地方却不少。第一、定义方法所用的泛型参数需要在修饰符之后添加,如上面的,public static <T>,如果有多个泛型参数,可如此定义<K,V>或者<T1,T2>。第二,不建议在泛型变量里添加其他类型,如下面的代码,将会引起编译错误(或隐含错误),如下:
public static <T> void fromArrayToCollection(T[] a,Collection<T> c){
  for (T t : a) {
   c.add(t);
   c.add(new Object()); 
  }
 }
    第三、看一下泛型方法的调用GenMethod.<Object>fromArrayToCollection(oa, co); 在方法前声明了泛型类型Object。不过因为编译器可以推断这个泛型类型,因此也可以这样写:
    GenMethod.fromArrayToCollection(oa, co)。
    为了加深对编译器推断泛型类型的了解,再看一下如下几个推断:
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();

// T 推断为String
fromArrayToCollection(sa, cs);

// T 推断为Object
fromArrayToCollection(sa, co);

Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();

//T 推断为Number
fromArrayToCollection(ia, cn);

//T 推断为Number
fromArrayToCollection(fa, cn);

//T 推断为Number
fromArrayToCollection(na, cn);

//T 推断为Object
fromArrayToCollection(na, co);

//编译错误,Number与String不能兼容
fromArrayToCollection(na, cs);
四、泛型参数的界限
    有时候,你会希望泛型类型只能是某一部分类型,比如操作数据的时候,你会希望是Number或其子类类型。这个想法其实就是给泛型参数添加一个界限。其定义形式为:
    <T extends BoundingType>
    此定义表示T应该是BoundingType的子类型(subtype)。T和BoundingType可以是类,也可以是接口。另外注意的是,此处的”extends“表示的子类型,不等同于继承。
    Demo:
public class Box<T> {

private T t;

public void set(T t) {
  this.t = t;
 }

public T get() {
  return t;
 }

public <U extends Number> void inspect(U u) {
  System.out.println("T: " + t.getClass().getName());
  System.out.println("U: " + u.getClass().getName());
 }

public static void main(String[] args) {
  Box<String> integerBox = new Box<>();
  integerBox.set("abc"); //能通过编译,因为T指定为String类型
//  integerBox.inspect("abc");//不能通过编译,因为U必须是Number类型或其子类
  integerBox.inspect(new Integer(10));
 }
}
    通过Box<T>,了解了如何为泛型参数添加一个界限。可问题也来了,既然限定了泛型参数的界限,那时候可以调用BoundingType(上指Number)的相对应的方法呢??答案是肯定的,如下:
public class NumberTest<T extends Integer> {
 private T num;
 
 public NumberTest(T num) { this.num = num;}
 
 public boolean isOdd(){
  return num.intValue()%2 == 1;
 }
 
 //....
}
    接着引入下一个问题,如何为泛型参数添加多个限制范围,多重限制范围格式如下:
    <T extends A & B & C>
    一个泛型参数可以有多重限制范围,使用“&”分隔。且限制范围中之多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。举例如下:
Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }
    如果BoundingType不是放在第一位,会产生编译异常:
class D <T extends B & A & C> { /* ... */ }  // 无法通过编译

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/9307b3733a59ee9ea5993502cfb73bf1.html