java 泛型 java核心技术 读书笔记

时间:2023-02-16 12:20:45

简单泛型类

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 void setFirst(T first) {
this.first = first;
}

public T getSecond() {
return second;
}

public void setSecond(T second) {
this.second = second;
}
}

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

 

泛型方法

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

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

 

类型变量的限定

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<T>(min, max);
}
}

以上代码中,将T限制为实现了Comparable接口的类。如:

public static void main(String[] args) {
Integer[] i = new Integer[]{12,345,563,24,64};
Pair<Integer> pair = ArrayAlg.minmax(i);
System.out.println(pair.getFirst() + " " + pair.getSecond());
}


如果将上面代码中第二行修改为int[] i = new int[]{12,345,563,24,64};,则第三行出现编译错误。

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

T extends Comparable & Serializable

 

泛型代码和虚拟机
无论何时定义一个泛型类型,都自动提供原始类型。原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型。

原始类型用第一个限定的类型变量来替换。如果没有给定限定就用Object替换。如Pair<T>中用Object替换 T,

注意:class Interval<Serializable & Comparable>中,原始类型用Serializable替换T,而编译器在必要时向Comparable插入强制类型转换。为了提高效率,应该将标签接口(即没有方法的接口)放在边界列表的末尾。

 

当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。如:

Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();

  擦除getFirst的返回类型后将返回Object类型。编译器自动插入Employee的强制类型转换。当存取一个泛型域时也要强制类型转换。如

Employee buddy = buddies.first;

 

方法public static <T extends Comparable> T min(T[] a)擦除后为public static Comparable min(Comparable[] a);

注意:方法擦除将带来两个复杂问题,请查看Java核心技术

Java泛型转换:

虚拟机中没有泛型 ,只有普通的类和方法

所有的类型参数都用到它们的限定类型替换

桥方法(查看Java核心技术)被合成保持多态

为保持类型安全性,必要时插入强制类型转换

 

约束与局限性

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

只要8种基本类型,当包装类型不能接受替换时,可以使用独立的类和方法处理它们。

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

  Pair<Integer> a = new Pair<Integer>();
  a instanceof Pair<String>;

 上面代码第2行将报错。

 

3.不能抛出也不能捕获泛型类实例

泛型类扩展Throwable都不合法。如public class Problem<T> extends Exception{      }       //ERROR--不能通过编译。

不能在catch子句中使用类型变量,如

public static <T extends Throwable> void dowork(Class<T> t){
try{
do work
}
catch(T e){//编译错误 -- 不能捕获这种类型

}
}

但,在异常声明中可以使用类型变量,如:

public static <T extends Throwable> void doWork(T t) throws T{//OK
try{
do work
}
catch(Throwable realCause){
t.initCause(realCause);
throw t;
}
}


4.参数化类型的数组不合法

不能声明参数化类型的数组,如:Pair<String>[] table = new Pair<String>[10];  //ERROR

5.不能实例化类型变量

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

public class Singleton<T> {
public static T getSingInstance(){

}
}

上面代码中方法getSingInstance无法通过编译。

7.注意擦除后的冲突

public class Pair<T> {
public boolean equals(T value){
return first.equals(value)&&second.equals(value);
}
}

从概念上讲,Pair有两个equals方法:

boolean equals(T)//定义在Pair<T>

boolean equals(Object)//从Object中继承

方法擦除将导致两个方法相冲突。可以重新命名引发错误的方法。


通配符类型(用于方法参数)

通配符Pair<? extends Employee>表示任何任何Pair类型,它的类型参数是Employee的子类,如Pair<Manager>,但不能是Pair<String>

注意:类型Pair<Manager>是Pair<? extends Employee>的子类型,而ArrayList<Manager>并不是ArrayList<Employee>的子类

如:

public static void printBuddies(Pair<? extends Employee> p){
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first + " " + second);
}
可以这样调用 :printBuddies(new Pair<Manager>());

 

超类型限定:? super Manager这个通配符限制为Manager的所有超类型

注意:当从超类型限定通配符中取出值时,只能将它赋给Object,如:Pair<? super Manager> p通配符中,Object o = p.getFirst();因为调用getFirst,返回的对象类型得不到保证。

java 泛型  java核心技术 读书笔记

直观地说,带有超类限定的通配符可以向泛型对象写入,如Pair<? super Manager> p中,可以这样调用:p.setFirst(new Manager());而带有子类型限定的通配符可以从泛型对象读取,如Pair<? extends Employee> p通配中,可以这样调用:Employee first = p.getFirst();


无限定通配符Pair<?>有方法:

? getFirst();

void setFirst();

getFirst的返回值只能赋给一个Object,而setFirst方法不能被调用,甚至不能用Object调用。注意:Pair可以用任意Object对象调用原始的Pair类的setFirst方法。

无限定通配符对于很多简单的操作非常有用。如,下面这个方法将用来测试一个Pair是否包含了指定的对象,它不需要实际的类型

    public static boolean hasNulls(Pair<?> p){

return p.getFirst() == null || p.getSecond() == null;

}



 通配符捕获

有时我们需要使用通配符中的类型,如编写 一个交换Pair元素的方法public static void swap(Pair<?> p),但?不能作为一个类型,就是说

       ? t = p.getFirst();

p.setFirst(p.getSecond());

p.setSecond(t);


 上面代码无法通过编译。

但我们可以通过一个辅助方法来完成工作,完整代码如下:

 public static void swap(Pair<?> p){

swapHelper(p);

}



public static <T> void swapHelper(Pair<T> p){

T t = p.getFirst();

p.setFirst(p.getSecond());

p.setSecond(t);

}



反射和泛型

Class类是泛型的。如String.class是Class<String>的唯一对象。类型参数十分有用。

public static <T> T createInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException{
return clazz.newInstance();
}
为了表达泛型类型声明,Java SE 5.0在java.lang.reflect包中提供了一个新的接口Type,包括

Class类,描述具体类型

TypeVariable接口,描述类型变量

WildcardType接口,描述通配符

ParameterizedType接口,描述泛型类型或接口类型

GenericArrayType接口,描述泛型数组。

具体情况请查阅API