8泛型

定义泛型类

1
2
3
4
5
6
7
8
9
10
11
12
public class Pair<T>
{
private T first;
private T second;
public Pair() { first = null ; second = null ; }
public Pairf(T first, T second) { this,
first = first; this.second = second; }
public T getFirstO { return first; }
public T getSecondO { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}

泛型方法

1
2
3
4
5
6
7
8
9
10
11
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}

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

String middle = ArrayAlg.getHiddle("john", "Q", "Public"); //可以省略 <String> 类型参数

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

类型变量的限定

类在前,接口在后
T extends Comparable & Serializable

泛型代码与虚拟机

类型擦除

翻译泛型表达式

翻译泛型方法

调用遗留代码

约束与局限性

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

没有 Pair, 只 有 Pair
原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储 double值。原因:与 Java 语言中基本类型的独立状态相一致。

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

只会检查类型是否是Pair不会检查泛型,如下示例

1
2
if (a instanceof Pair<String>) //Error
Pair<String> p = (Pair<String>) a; // Warning-can only test that a is a Pair
1
2
3
List<String> stringList = Arrays.asList("1", "2");
List<Integer> integerList = Arrays.asList(1, 2);
System.out.println(stringList.getClass()==integerList.getClass()); // true 两次调用 getClass 都将返回 Pair.class

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

Pair<String>[] table = new Pair<String>[10]; // Error

可以声明类型为 Pair<String>[]的变量 但不能用new Pair<String>[10]初始化这个变量
如果需要收集参数化类型对象, 只有一种安全而有效的方法:使用 ArrayList:ArrayList<Pair>

不能实例化类型变置

public Pair() { first = new T(); second = new T(); } // Error
解决办法:提供一个构造器表达式,通过反射调用 Clasmewlnstance 方法来构造泛型对象

1
2
3
4
5
6
7
public static <T> Pair<T> makePair(Class<T> cl)
{
try { return new Pair<>(d.newInstance(). cl.newInstance());}
catch (Exception ex) { return null; }
}

Pair<String> p = Pair.makePair(String.class);

不能构造泛型数组

T[] arr = new T[10];// ERROR
解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyClass<T> {
T[] list1;

//泛型数组1
public void init(IntFunction<T[]> func, int count) {
T[] m = func.apply(count);
list1 = m;
}

/// 泛型数组2
ArrayList<T> list2 = new ArrayList<>();
}

MyClass<String> mc = new MyClass<>();
mc.init(String[]::new, 10);

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

以下代码无效。
禁止使用带有类型变量的静态域和方法

1
2
3
4
5
6
7
8
9
10
public class Singleton<T>
{
private static T singlelnstance; // Error
public static T getSinglelnstance() // Error
{
if (singleinstance == null) construct new instance of T
return singlelnstance;
}
}

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

泛型类型不可扩展 Exception,throwable

1
2
3
public class Problem<T> extends Exception { // ERROR
/* . . . */
}

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

1
2
3
try {}
catch (T e) // Error can 't catch type variable
{}

可以消除对受查异常的检查

1
2
ArrayList<Parent1> aa = new ArrayList<Sun2>();// ERROR
Parent1[] aaaa = new Sun2[1]; // OK

继承规则

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

通配符类型

通配符概念

Pair<? extends Employee〉

1
2
3
4
5
6
7
Pair<PParent> pair = new Pair<>(new PSun(), new PSun());  // OK
Pair<PSun> pair2 = new Pair<>(new PSun(), new PSun()); // OK
pair = pair2; // ERROR

Pair<? extends PParent> pairs = pair2; // OK
pairs= pair; // OK
pairs.setFirst(new PParent()); // compile-time ERROR ,编译器只知道需要Employee的子类型,但不知道具体类型,拒绝传递任何特定的类型,?不能用来匹配

通配符的超类型限定

指定一个超类型限定(supertypebound):? super Manager

限定符包含自己

超类限定符 ?supper getter方法受限
子类限定符 ?extends setter方法受限

无限定通配符

例如,Pair 类型 Pair 有以下方法:(伪代码)
? getFi rst()
void setFirst⑺

getFirst 的返回值只能赋给一个 Object。setFirst 方法不能被调用, 甚至不能用 Object 调用。可以调用set(null)

用途例如:

1
2
3
4
5
6
7
public static boolean hasNulls(Pair<?> p)
{
return p.getFirstO = null || p.getSecondO =null;
}

//转换成泛型方法
public static <T> boolean hasNulls(Pair<T> p)

上例中 通配符的版本可读性更强

问题

怎么理解:Pair<?> 和 Pair 本质的不同在于: 可以用任意 Object 对象调用原始 Pair 类的 setObject方法。

反射和泛型

反射允许你在运行时分析任意的对象。如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除。