Fork me on GitHub

Java编程思想———泛型

泛型实现了参数化类型的概念。

根据泛型应用的不同,可分为:

  • 泛型类

    1
    2
    3
    class ClassParameter<T> {
    public T f(T arg) { return arg; }
    }
  • 泛型接口

    1
    2
    3
    interface InterfaceParameter<T> {
    T f(T arg);
    }
  • 泛型方法

    1
    2
    3
    class MethodParameter {
    public static <T> T f(T arg) { return arg; }
    }

泛型的擦除

1
2
3
4
5
6
7
8
9
10
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}

//output
true

Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此,List<String>List<Integer>在运行时事实上是相同的类型。这两种形式都被擦除成它们的原生类型,即List

在基于擦除的实现中,泛型类型被当作第二类类型处理,即不能在某些重要的上下文环境中使用的类型。

泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。

擦除的代价是显著的,泛型不能用于显示地引用运行时类型的操作之中,例如转型、instanceof操作和new表达式。

问题

在使用Java泛型时会出现的各类问题。

  1. 任何基本类型都不能作为类型参数。

解决办法是使用基本类型的包装器类及自动装箱机制。ArrayList<Integer>

  1. 实现参数化接口

一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两种变体会成为相同的接口。

1
2
3
4
5
interface Payable<T> {}

class Employee implements Payable<Employee> {}
class Hourly extends Employee implements Payable<Hourly> {}
//Hourly无法编译
  1. 转型和警告

使用带有泛型类型参数的转型或instanceof不会有任何效果。

  1. 重载
1
2
3
4
5
public class UseList<W,T> {
void f(List<T> v) {}
void f(List<W> v) {}
}
//无法编译

由于擦除的原因,重载方法将产生相同的类型签名。

当被擦除的参数不能产生唯一的参数列表时,必须提供明显有区别的方法名:

1
2
3
4
public class UseList2<W,T> {
void f1(List<T> v) {}
void f2(List<W> v) {}
}
  1. 基类劫持了接口
1
2
3
4
5
6
7
class Pet implements Comparable<Pet> {
public int compareTo(Pet arg) { return 0; }
}

public class ComparablePet implements Comparable<ComparablePet> {
public int compareTo<ComparablePet arg> { return 0; }
}
1
2
3
4
class Cat extends ComparablePet implements Comparable<Cat> {
//Error: Comparable不能被不同的参数Cat和Pet继承
public int compareTo<Cat arg> { return 0; }
}

数组与泛型

不能实例化具有参数化类型的数组:

1
Peel<Banana>[] peels = new Peel<Banana>[10]; //非法

擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。

但是,可以参数化数组本身的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ClassParameter<T> {
public T[] f(T[] arg) { return arg; }
}

class MethodParameter {
public static T[] f(T[] arg) { return arg; }
}

public class ParameterizedArrayType {
public static void main(String[] args) {
Integer[] ints = { 1, 2, 3, 4, 5 };
Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Integer[] ints2 = new ClassParameter<Integer>().f(ints);
Double[] doubles2 = new ClassParameter<Double>().f(doubles);
ints2 = MethodParameter.f(ints);
doubles2 = MethodParameter.f(doubles);
}
}
求鼓励,求支持!