前言:泛型是JDK1.5开始,Java便允许定义泛型类,泛型接口,泛型方法。可以使用泛型对API中一些接口和类进行修改。

  • JDK1.5之前的java.lang.Comparable
1
2
3
4
package java.lang;
public interface Comparable{
public int compareTo(Object o)
}
  • JDK1.5的java.lang.Comparable
    1
    2
    3
    4
    package java.lang;
    public interface Comparable<T>{
    public int compareTo(T o)
    }

泛型(参数化类型的能力)

  • 从形式泛型类型转换为实际具体类型 称为泛型实例化
1
List<T> --> List<Integer>

泛型的优点:

使用泛型的主要优点是:能够在编译时而不是在运行时检查出错误,泛型类或方法规定了用户在使用这些类或方法的对象类型,如果我们使用了一个不兼容的类型,编译器就会检测出这个错误。同时使用泛型,我们可以保证软件的可靠性和可读性,保证了程序不是运行时产生错误,而是编译时产生错误。

1.例子一:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
Comparable c = new Date();
System.out.println(c.compareTo("Red"));
/*
曝出异常
ClassCastException: java.lang.String cannot be cast to java.util.Date
*/
}

原因分析:这样的代码会产生运行错误,因为一个字符串和一个日期时间比较,显而易见会存在异常

2.例子二:

1
2
3
4
public static void main(String[] args) {
Comparable<Date> c = new Date();
System.out.println(c.compareTo("red"));
}

对比两个例子代码,很明显泛型可以保证程序的健壮性,如果我们不适用泛型,默认泛型是Object,但是在转换就有很多弊端,很有可能就会曝出异常!


泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
		/*使用泛型方法要将泛型放到方法名后面*/
public class GenericStack<E1,E2> {
public GenericStack() {
/*构造方法仍然这样写*/
}

public <E1,E2> void add(){
/*使用泛型方法要将泛型放到返回类型前面*/
}

public static void main(String[] args) {
GenericStack<String, Integer> gen = new GenericStack<String,Integer>();
gen.<String, Integer>add();
/*调用泛型方法,需要将实际类型放到尖括号内并作为方法名的前缀*/
}

/*将泛型指定为另外一种类型的子类型,被成为受限的泛型类型*/
public static <E extends Object> boolean isNull()
{
return false;
}
}
泛型类

原始类型和向后兼容

  • 原始类型:不使用类型参数的泛型类
  • Java允许使用原始类型向后兼容,但是原始类型并不安全,更好的方法是使用泛型
1
2
3
4
5
6
7
8
9
10
public class Max {
public static Comparable max(Comparable o1, Comparable o2)
{
if(o1.compareTo(o2) > 0)
return o1;
else
return 02;
}
}
/*如果我们调用方法Max.max("welcome",23) 便会产生一个错误,因此不建议使用原始类型*/

通配泛型

1.为什么使用通配泛型?

代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class WildCardDemo1 {
public static void main(String[] args) {
GenericStack<Integer> intStack = new GenericStack<Integer>();
intStack.push(1);
intStack.push(2);
intStack.push(-2);
System.out.print("The max number is " + max(intStack));
}

public static double max(GenericStack<Number> stack)
{
double max = stack.pop().doubleValue();

while(!stack.isEmpty())
{
double value = stack.pop().doubleValue();
if(value > max)
max = value;
}

return max;
}
}
/*分析:第8行会出现编译错误,最开始自己也没想通,所以就拿来这道题目来作例题,*/
/*因为intStack不是GenericStack<Number>的实例*/

为了避免上面例子中的问题,我们采用通配泛型类型,通配泛型类型有三种:

  • ?
  • ? Extends T 表示T或T的一个未知子类型
  • ? super T(其中T是某个泛型类型),表示T或T的一个未知父类型

第一种称为非受限通配, 第二三种称为受限通配,也叫下限通配

将max方法修改成public static double max(GenericStack? Extends Number> Stack)便可以解决问题,其中? Extends Number 表示Number或者Number子类型的通配类型

代码2:

思考以下代码是否正确?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class WildCardDemo2 {
public static void main(String[] args) {
GenericStack<Integer> intStack = new GenericStack<Integer>();
intStack.push(1);
intStack.push(2);
intStack.push(-2);

print(intStack);
}

public static void print(GenericStack<Object> stack)
{
while(!stack.isEmpty())
System.out.println(stack.pop() + " ");
}
}

上面代码是不合法的,尽管我们的stack的泛型类型是?(等价于<? Extends Object>) 我们intStack的泛型类型是Integer,但是如果调用print(intStack)将会报错,因为GenericStack并不是GenericStack的子类型,所以我们需要使用<? super T>通配符


泛型注意事项

  1. 泛型类型必须是引用类型,不可以使用基本数据类型来进行替换
  2. 泛型类中,构造方法不需要加上泛型
  3. 泛型类有多个参数的时候,我们需要