泛型

前言

一般类方法只能使用具体的类型,无法适用于多种类型的代码。即使是继承体系或者接口实现等方法,还是会有很多约束,无法编写出更通用的代码。Java5以后,为创造容器类促成了泛型的产生。

简介

泛型字面上含义指一般的类型,通过一些符号代码某很多的类型,可以有边界也可以指所有类型。容器本身就支持各种各样的类型对象,泛型在容器中广泛应用。泛型可被应用与接口、方法,可以被擦除或者添加界限。

泛型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 泛型接口
public interface Generator<T> {
T getNext();
}

public class Fibonacci implements Generator<Integer> {

private int count = 0;
@Override
public Integer getNext() {
return fab(count++);
}
private Integer fab(int n){
if(n < 2) return 1;
return fab(n-2)+fab(n-1);
}
public static void main(String[] args) {
Fibonacci fibonacci = new Fibonacci();
for(int i = 0; i < 10; i++){
int n = fibonacci.getNext();
System.out.print(n + " ");
}
}
}

输出结果

1
2
3
"E:\Program Files\Java\jdk1.8.0_91\bin\java" "-javaagent:D:\Program ……
1 1 2 3 5 8 13 21 34 55
Process finished with exit code 0

泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GeneralMethod {
// 泛型方法
public <T> String getClassName(T t){
String name = t.getClass().getName();
System.out.println(name);
return name;
}
public static void main(String[] args) {
GeneralMethod generalMethod = new GeneralMethod();
generalMethod.getClassName("");
generalMethod.getClassName(0);
generalMethod.getClassName(0L);
generalMethod.getClassName(2.0);
}
}

输出结果

1
2
3
4
5
6
7
"E:\Program Files\Java\jdk1.8.0_91\bin\java" "-javaagent:D:\Program ……
java.lang.String
java.lang.Integer
java.lang.Long
java.lang.Double

Process finished with exit code 0

擦除

java泛型是通过擦除实现的,容器中泛型被指定为具体类型时会被擦除。List\与List\在java中都是List\,他们的类型相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class erase {

public static void main(String[] args){
List<Integer> integers = new ArrayList<>();
List<String> strings = new ArrayList<>();
Map<Integer, String> integerStringMap = new HashMap<>();
Map<String, String> stringStringMap = new HashMap<>();
System.out.println(Arrays.toString(integerStringMap.getClass()
.getTypeParameters()));
System.out.println(integerStringMap.getClass()
== stringStringMap.getClass());
System.out.println(integers.getClass() == strings.getClass());
System.out.println(integers.equals(strings));
}
}

输出结果为

1
2
3
4
5
6
7
"E:\Program Files\Java\jdk1.8.0_91\bin\java" "-javaagent:D:\Program ……
[K, V]
true
true
true

Process finished with exit code 0

边界

泛型没有边界时只能使用Object的方法,无法使用指定类型的方法,若想使用指定类型的方法必须加上边界。java泛型通过extends关键字添加边界,具体用法如下代码所示,泛型的边界为String,则泛型可以使用String特有的charAt()方法。

1
2
3
4
5
6
7
public class boader {

public <T extends String> void getChar(T s){
char c = s.charAt(0);
System.out.println(c);
}
}

通配符

java中用表示通配符,通配符可与泛型结合使用,也可单独使用,既可以是无界的也可以加上范围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 无界通配符
public Class<?> getClass(String className){
Class<?> cls = className.getClass();
List<?> list = new ArrayList<>();
Map<?, ?> map = new HashMap<>();
return cls;
}
// 有界通配符
public <T extends String> void getInstance(Class<T> tClass) throws Exception{
// T可以单独使用
T t = tClass.newInstance();
List<T> tList = new ArrayList<>();
// 返回结果是String子类即可
List<? extends String> list = tList;
}

通配符与泛型使用起来既有共同点,也有区别。他们都可以代表所有的类型(无界),也可限定某些类型(有界)。但泛型一般必须要在接口或者方法头部用尖括号符号<>声明,而且泛型可以不配合容器类或者Class等类使用,泛型可以单独使用,但通配符不可以单独使用,代码如下所示。而且通配符一般用于返回结果。当返回结果的类型不确定时,而又不想在方法或者接口处声明泛型时,可以用通配符来实现。

1
2
3
4
5
Class<T> cls;
// 合法
T t = cls.newInstance();
// 不合法
? t = cls.newInstance();