“核心技术:泛型”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第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 & Serializableimplements 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()

编译器把这个方法调用翻译为两条虚拟机指令:

  1. 对原始方法Pair.getFirst 的调用。
  2. 将返回的Object 类型强制转换为Employee 类型。

翻译泛型方法

类型擦除也会出现在泛型方法中:

public static <T extends ComparableT 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 泛型转换的事实:

  • 虚拟机中没有泛型, 只有普通的类和方法
  • 所有的类型参数都用它们的限定类型替换
  • 桥方法被合成来保持多态
  • 为保持类型安全性,必要时插人强制类型转换

调用遗留代码

约束与局限性

泛型类的继承规则

通配符类型

反射和泛型