当前位置: 代码迷 >> 综合 >> JAVA 泛型程序设计 -- 《JAVA 核心技术 卷I》
  详细解决方案

JAVA 泛型程序设计 -- 《JAVA 核心技术 卷I》

热度:95   发布时间:2023-12-01 20:58:57.0

1 泛型程序设计

泛型程序设计(Generic Programming)意味着编写的代码可以被很多不同类型的对象所重用。
在 JAVA 增加泛型之前,泛型是用继承来实现的。
ArrayList 类只维护一个 Object 引用的数组:

public class Test{public static void main(String[] args) {ArrayList list = new ArrayList();list.add("Kaven");list.add(1); String str = (String)list.get(0);}
}

这种方法实现泛型存在两个问题:

  1. 获取一个元素时必须进行强制类型转换(当然除了 Object)。
  2. 没有错误检查,可以向 ArrayList 中添加任何类型的元素。

泛型程序设计提供了一个更好的解决方案:类型参数(Type Parameters)。
ArrayList 类有一个类型参数用来指示元素的类型:

public class Test{public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Kaven1");list.add("kaven2"); String str = list.get(0);}
}

这使得代码具有更好的可读性,人们一看就知道这个 ArrayList 中包含的是 String 对象。
在 JAVA SE7 及以后的版本中,构造方法中可以省略泛型类型,省略的类型可以从变量的类型推断得出。
编译器也可以很好地利用这个信息,当调用 get 方法时,不需要进行强制类型转换,编译器就知道返回值类型为 String,而不是Object。
编译器还知道 ArrayList< String > 中 add 方法有一个类型为 String 的参数,这将比使用 Object 类型的参数安全一些,编译器可以进行检查,避免插入一个错误类型的对象。

2 定义简单泛型类

一个泛型类(Generic Class)就是具有一个或多个类型变量的类。
如下:

public class Test<T>{private T first;private T second;public Test(){first = null;second = null;}public Test(T first,T second){this.first = first;this.second = second;}public T getFirst(){return first;}public T getSecond(){return second;}
}

类型变量通常使用大写形式,且比较短。
在 JAVA 库中,使用变量 E 表示集合的元素类型,K 和 V 分别表示表的关键字与值的类型,T (需要时还可以用临近的字母 U 和 S )表示 “任意类型”。

3 泛型方法

实际上,还可以定义一个带有类型参数的简单方法:

public class Test{public static <T> T get(T t){return t;}
}

泛型方法可以定义在普通类中,也可以定义在泛型类中。
当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型:

public class Test{public static <T> T get(T t){return t;}public static void main(String[] args) {String str = Test.<String>get("kaven");}
}

在这种情况下,方法调用中可以省略 < String > 类型参数,编译器有足够的信息能够推断出所调用的方法。

偶尔也会出错:

public class Test{public static <T> T get(T... t){return t[t.length/2];}public static void main(String[] args) {//double str = Test.get(1.5 , 1 , 2 ); error}
}

错误信息:

类型不匹配:不能从 Number&Comparable< ? > 转换为 double

简单地说,编译器将会自动打包参数为 1 个 Double 和 2 个 Integer 对象,而后寻找这些类的共同超类型,事实上,找到 2 个这样的超类型 Number 和 Comparable接口,其本身也是一个泛型类型。
在这种情况下,可以采取的补救措施是将所有的参数写为 double 值。

4 类型变量的限定

有时,类或方法需要对类型变量加以约束。
下面是一个典型的例子:

public class Test{public static <T extends Comparable<T>> T min(T[] a){if(a==null || a.length==0) return null;T smallest = a[0];for(int i = 1;i < a.length; i++) if(smallest.compareTo(a[i]) > 0) smallest = a[i];return smallest;}
}

根据上面程序可以看出,我们要计算数组中的最小元素,所以类型变量 T 之间必须可以比较大小,为了解决这个问题,可以让类型变量 T extends Comparable< T >。
所以可以通过对类型变量 T 设置一些限定来符合一些要求。
现在,泛型的 min 方法只能被实现了 Comparable 接口的类的数组调用。

< T extends BoundType>

上面表示 T 应该是限定类型的子类型,T 和限定类型可以是类,也可以是接口。
选择关键字 extends 的原因是更接近子类的概念。

< T extends BoundType1 & BoundType2 ,U extends BoundType3>

限定类型用 “&” 分隔,而逗号用来分隔类型变量。
在 JAVA 的继承中,一个类可以根据需要拥有多个接口超类型,但是只能继承一个父类,在这里也一样,限定类型中至多有一个类,可以有多个接口,如果限定类型中有类,那么它必须是限定列表的第一个。

  相关解决方案