“核心技术:泛型”的版本间差异
跳到导航
跳到搜索
第52行: | 第52行: | ||
== 泛型代码和虚拟机 == | == 泛型代码和虚拟机 == | ||
=== 类型擦除 === | |||
无论何时定义一个泛型类型, 都自动提供了一个相应的原始类型( raw type )。 | |||
* 原始类型的名字就是删去类型参数后的泛型类型名。 | |||
* 擦除(erased) 类型变量, 并替换为限定类型(无限定的变量用Object)。 | |||
如: | |||
<syntaxhighlight lang="java"> | |||
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; } | |||
} | |||
</syntaxhighlight> | |||
擦除为: | |||
<syntaxhighlight lang="java"> | |||
public class Pair | |||
{ | |||
private Object first; | |||
private Object second; | |||
public Pair() { first = null ; second = null ; } | |||
public Pair(Object first, Object second) | |||
{ | |||
this,first = first; | |||
this.second = second; | |||
} | |||
public Object getFirst() { return first; } | |||
public Object getSecond() { return second; } | |||
public void setFirst(Object newValue) { first = newValue; } | |||
public void setSecond(Object newValue) { second = newValue; } | |||
} | |||
</syntaxhighlight> | |||
* 原始类型用第一个限定的类型变量来替换, 如果没有给定限定就用Object 替换。 | |||
例: | |||
<syntaxhighlight lang="java"> | |||
public class Interval <T extends Comparable & Serializable〉implements Serializable | |||
{ | |||
private T lower; | |||
private T upper; | |||
... | |||
public Interval (T first , T second) | |||
{ | |||
if (first .compareTo(second) <= 0) { lower = first ; upper = second; } | |||
else { lower = second; upper = first; } | |||
} | |||
} | |||
</syntaxhighlight> | |||
擦除为: | |||
<syntaxhighlight lang="java"> | |||
public class Interval implements Serializable | |||
{ | |||
private Comparable lower; | |||
private Coiparable upper; | |||
... | |||
public Interval (Coiparable first, Coiparable second) { . . . } | |||
} | |||
</syntaxhighlight> | |||
=== 翻译泛型表达式 === | |||
对于如下表达式: | |||
<syntaxhighlight lang="java"> | |||
Pair<Employee> buddies = . .; | |||
Employee buddy = buddies.getFirst(); | |||
</syntaxhighlight> | |||
编译器把这个方法调用翻译为两条虚拟机指令: | |||
# 对原始方法Pair.getFirst 的调用。 | |||
# 将返回的Object 类型强制转换为Employee 类型。 | |||
=== 翻译泛型方法 === | |||
类型擦除也会出现在泛型方法中: | |||
<syntaxhighlight lang="java"> | |||
public static <T extends Comparable〉T min(T[] a) | |||
</syntaxhighlight> | |||
擦除为: | |||
<syntaxhighlight lang="java"> | |||
public static Comparable min(Comparable[] a) | |||
</syntaxhighlight> | |||
方法的擦除带来了两个复杂问题。如: | |||
<syntaxhighlight lang="java"> | |||
class Datelnterval extends Pair<LocalDate> | |||
{ | |||
public void setSecond (Local Date second) | |||
{ | |||
if (second.compareTo(getFi rst()) >= 0) | |||
super. setSecond (second); | |||
} | |||
... | |||
} | |||
</syntaxhighlight> | |||
这个类擦除后变成: | |||
<syntaxhighlight lang="java"> | |||
class Dateinterval extends Pair// after erasure | |||
{ | |||
public void setSecond (Local Date second) { . . . } | |||
... | |||
} | |||
</syntaxhighlight> | |||
而Datelnterval同时存在一个从Pair继承的setSecond方法,即: | |||
<syntaxhighlight lang="java"> | |||
pub1ic void setSecond (Object second) | |||
</syntaxhighlight> | |||
考虑下面的语句序列: | |||
<syntaxhighlight lang="java"> | |||
Datelnterval interval = new Datelnterval (...) ; | |||
Pair<LocalDate> pair= interval; // OK--assignment to superclass | |||
pair.setSecond (aDate); | |||
</syntaxhighlight> | |||
在这里,'''类型擦除与多态发生了冲突。'''要解决这个问题,就需要'''编译器在Datelnterval类中生成一个桥方法(bridgemethod)''': | |||
<syntaxhighlight lang="java"> | |||
public void setSecond(Object second) | |||
{ | |||
setSecond((Date) second); | |||
} | |||
</syntaxhighlight> | |||
有关Java 泛型转换的事实: | |||
* '''虚拟机中没有泛型, 只有普通的类和方法'''。 | |||
* '''所有的类型参数都用它们的限定类型替换'''。 | |||
* '''桥方法被合成来保持多态'''。 | |||
* '''为保持类型安全性,必要时插人强制类型转换'''。 | |||
=== 调用遗留代码 === | |||
== 约束与局限性 == | == 约束与局限性 == |
2020年10月22日 (四) 13:08的版本
为什么要使用泛型
泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。
类型参数( type parameters )。ArrayList 类有一个类型参数用来指示元素的类型:
ArrayList<String> files = new ArrayList<String>();
- 在Java SE 7 及以后的版本中, 构造函数中可以省略泛型类型:
ArrayList<String> files = new ArrayList<>();
定义简单泛型类
一个泛型类( generic class ) 就是具有一个或多个类型变量的类。
- 换句话说,泛型类可看作普通类的工厂。
泛型方法
- 类型变量放在修饰符(这里是public static ) 的后面,返回类型的前面。
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}
泛型变量的限定
是将T 限制为实现了Comparable 接口(只含一个方法compareTo 的标准接口)的类:
public static <T extends Coiparab1e> T min(T[] a) . . .
- 使用关键字extends 而不是implements
- T 和绑定类型可以是类, 也可以是接口
- 一个类型变量或通配符可以有多个限定:
T extends Comparable & Serializable
- 限定类型用“&”分隔,而逗号用来分隔类型变量。
泛型代码和虚拟机
类型擦除
无论何时定义一个泛型类型, 都自动提供了一个相应的原始类型( raw type )。
- 原始类型的名字就是删去类型参数后的泛型类型名。
- 擦除(erased) 类型变量, 并替换为限定类型(无限定的变量用Object)。
如:
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; }
}
擦除为:
public class Pair
{
private Object first;
private Object second;
public Pair() { first = null ; second = null ; }
public Pair(Object first, Object second)
{
this,first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}
- 原始类型用第一个限定的类型变量来替换, 如果没有给定限定就用Object 替换。
例:
public class Interval <T extends Comparable & Serializable〉implements Serializable
{
private T lower;
private T upper;
...
public Interval (T first , T second)
{
if (first .compareTo(second) <= 0) { lower = first ; upper = second; }
else { lower = second; upper = first; }
}
}
擦除为:
public class Interval implements Serializable
{
private Comparable lower;
private Coiparable upper;
...
public Interval (Coiparable first, Coiparable second) { . . . }
}
翻译泛型表达式
对于如下表达式:
Pair<Employee> buddies = . .;
Employee buddy = buddies.getFirst();
编译器把这个方法调用翻译为两条虚拟机指令:
- 对原始方法Pair.getFirst 的调用。
- 将返回的Object 类型强制转换为Employee 类型。
翻译泛型方法
类型擦除也会出现在泛型方法中:
public static <T extends Comparable〉T min(T[] a)
擦除为:
public static Comparable min(Comparable[] a)
方法的擦除带来了两个复杂问题。如:
class Datelnterval extends Pair<LocalDate>
{
public void setSecond (Local Date second)
{
if (second.compareTo(getFi rst()) >= 0)
super. setSecond (second);
}
...
}
这个类擦除后变成:
class Dateinterval extends Pair// after erasure
{
public void setSecond (Local Date second) { . . . }
...
}
而Datelnterval同时存在一个从Pair继承的setSecond方法,即:
pub1ic void setSecond (Object second)
考虑下面的语句序列:
Datelnterval interval = new Datelnterval (...) ;
Pair<LocalDate> pair= interval; // OK--assignment to superclass
pair.setSecond (aDate);
在这里,类型擦除与多态发生了冲突。要解决这个问题,就需要编译器在Datelnterval类中生成一个桥方法(bridgemethod):
public void setSecond(Object second)
{
setSecond((Date) second);
}
有关Java 泛型转换的事实:
- 虚拟机中没有泛型, 只有普通的类和方法。
- 所有的类型参数都用它们的限定类型替换。
- 桥方法被合成来保持多态。
- 为保持类型安全性,必要时插人强制类型转换。