上一篇文章介绍了Java泛型中的基础及原理,本文将继续研究有关Java泛型的内容。本文的主要内容有:
- 泛型的特性
- 泛型通配
- 泛型类与普通类的不同点,也是日常开发要主要的点
泛型特性
泛型的兼容性
首先要强调的是,泛型是编译时才会检查合法性,编译后会直接擦除泛型信息。正由于这一点,所以在使用Eclipse编写源代码时,如果代码不合法,它会直接提示我们。Java编译器是向后兼容的,也就是低版本的源代码可以用高版本编译器进行编译。下面来看看那些兼容性代码。
- 引用和实例化都不包含泛型信息。
import java.util.ArrayList;
import java.util.List;
/**
* @author xialei
* @version 1.0 2016年6月28日下午9:03:44
*/
public class Compatibility {
public static void main(String[] args) {
// 下面编译通过
List list1 = new ArrayList();
list1.add("123");
list1.add(1);
}
}
上面的这段代码是可以通过编译的,这是JDK1.4之前的写法,所以可以验证JDK1.5之后的编译器是可以兼容JDK1.4之前的源代码的。不过,笔者在JDK1.8.x版本的编译器进行编译时,会抛出如下所示的警告信息。很显然,如果类被定义成泛型类,但是在实际使用时不使用泛型特性,这是不推荐的做法!
注: Compatibility.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
- 引用使用泛型,实例化不使用泛型。
import java.util.ArrayList;
import java.util.List;
/**
* @author xialei
* @version 1.0 2016年6月28日下午9:03:44
*/
public class Compatibility {
public static void main(String[] args) {
// 编译不通过
List<String> list2 = new ArrayList();
list2.add("123");
list2.add(1); // 这里出错
}
}
上面的代码编译不通过,由于对引用使用了泛型,其中的所能容纳的对象必须为String 类型。这种写法实际上跟完整写法的作用一致,不过Eclipse仍然会警告。
- 引用不使用泛型,实例化使用泛型。
import java.util.ArrayList;
import java.util.List;
/**
* @author xialei
* @version 1.0 2016年6月28日下午9:03:44
*/
public class Compatibility {
public static void main(String[] args) {
// 编译通过
List list3 = new ArrayList<String>();
list3.add("123");
list3.add(1);
}
}
上面的这段代码可以编译通过,其效果与1(不使用泛型)完全一致。结合2、3可以知道,编译时只能做引用的类型检查,而无法检查引用所指向对象的实际类型。
泛型与继承
在使用泛型时,引用的参数类型与实际对象的参数类型要保持一致(通配符除外),就算两个参数类型是继承关系也是不允许的。看看下面的2行代码,它们均不能通过编译。
ArrayList<String> arrayList1 = new ArrayList<Object>(); //编译错误
ArrayList<Object> arrayList1 = new ArrayList<String>(); //编译错误
下面来探讨一下为什么不能这么做。
- 第1种情况,如果这种代码可以通过编译,那么调用
get()
方法返回的对象应该是String
,但它实际上可以存放任意Object类型的对象,这样在调用类型转换指令时会抛出ClassCastException
。这样可以不是那么明显,来看看下面的代码。arrayList1中实际存放的Object对象,所以在进行类型转换时会抛出异常。这原本就是泛型想要极力避免的问题,所以Java允许这种写法。
ArrayList<Object> arrayList1 = new ArrayList<Object>();
arrayList1.add(new Object());
ArrayList<String> arrayList2 = arrayList1; //编译错误
- 第2种情况。虽然String类型的对象转换为Object不会有任何问题,但是这有什么意义呢?我们原本想要用String对象的方法,但最终将其赋予了一个Object类型的引用。如果需要使用String中的某些方法,必须将Object强制转换为String。这样不会抛出异常,但是却违背了泛型设计的初衷。
泛型与多态
下面来考虑一下泛型中多态问题。普通类型的多态是通过继承并重写父类的方法来实现的,泛型也不例外,下面是一个泛型多态示例。
/*
* 个人主页:http://hinylover.space
*
* Creation Date: 2016年7月2日 下午7:50:35
*/
package demo.blog.java.generic;
/**
* @author xialei
* @version 1.0 2016年7月2日下午7:50:35
*/
public class Father<T> {
public void set(T t) {
System.out.println("I am father, t=" + t);
}
public T get() {
return null;
}
}
/*
* 个人主页:http://hinylover.space
*
* Creation Date: 2016年7月2日 下午7:50:57
*/
package demo.blog.java.generic;
/**
* @author xialei
* @version 1.0 2016年7月2日下午7:50:57
*/
public class Son extends Father<String> {
@Override
public void set(String t) {
super.set(t);
System.out.println("I am son.");
}
@Override
public String get() {
return super.get();
}
public static void main(String[] args) {
Father<String> father = new Son();
father.set("hello world");