泛型是jdk1.5以后出来的新特性,Java中的泛型是个假泛型,仅仅只是在编译期做了语法检查。基本上,不管在list<>里面写什么类型,编译通过后运行时全部都是object。泛型做了语法、类型编译期间的检查,避免了在运行时出现ClassCastException。
先了解几个问题?
(1)List<Object>和原始类型List之间的区别:
在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查,通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String或Integer。你可以把任何带参数的类型传递给原始类型List,但却不能把List<String>传递给接受List<Object>的方法,因为会产生变异错误
(2)Java中List<?>和List<Object>之间的区别
List<?> 是一个未知类型的List,而List<Object>其实是任意类型的List。你可以把List<String>, List<Integer>赋值给List<?>,却不能把List<String>赋值给List<Object>。
(3)Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List<String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
(4)可以把List<String>传递给一个接受List<Object>参数的方法吗?
不可以,因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储Strings。
(5)什么是泛型中的限定通配符和非限定通配符?
限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界,可以接受任何继承自T的类型的List;例如List<? extends Number>可以接受List<Integer>或List<Float>。另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面<?>表示了非限定通配符,因为<?>可以用任意类型来替代
*泛型的使用:java泛型的两种用法:List<T>是泛型方法,List<?>是限制通配符
1>泛型类
先看一个没有泛型的情况下的容器类如何定义:
public class Text {
private String key;
private String value;
public Text (String k, String v) {
key = k;
value = v;
}
}
Text
类保存了一对key-value
键值对,但是类型是定死的,也就说如果我想要创建一个键值对是String-Integer
类型的,当前这个Text
是做不到的,必须再自定义。那么这明显重用性就非常低。
当然,我可以用Object
来代替String
,并且在Java SE5之前,我们也只能这么做,由于Object
是所有类型的基类,所以可以直接转型。但是这样灵活性还是不够,因为还是指定类型了,只不过这次指定的类型层级更高而已,有没有可能不指定类型?有没有可能在运行时才知道具体的类型是什么?
泛化:
public class Text<K, V> {
private K key;
private V value;
public Text(K k, V v) {
key = k;
value = v;
}
}
在编译期,是无法知道K
和V
具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。可以看一下现在Text
类对于不同类型的支持情况:
public class Main {
public static void main(String[] args) {
Text<String, String> c1 = new Text<String, String>("name", "findingsea");
Text<String, Integer> c2 = new Text<String, Integer>("age", 24);
Text<Double, Double> c3 = new Text<Double, Double>(1.1, 2.2);
}
}
2>泛型接口
在泛型接口中,生成器是一个很好的理解,看如下的生成器接口定义:
列1:
public interface Generator<T> {
public T next();
}
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
public class Main {
public static void main(String[] args) {
FruitGenerator generator = new FruitGenerator();
System.out.println(generator.next());
}
}
列2:
interface Show<T,U>{
void show(T t,U u);
}
class ShowTest implements Show<String,Date>{
@Override
public void show(String str,Date date) {
System.out.println(str);
System.out.println(date);
}
}
public static void main(String[] args) throws ClassNotFoundException {
ShowTest showTest=new ShowTest();
showTest.show("Hello",new Date());
}
3>泛型方法
如果使用泛型方法可以取代将整个类泛化,那么应该优先采用泛型方法。下面来看一个简单的泛型方法的定义:
public class Main {
public static <T> void out(T t) {
System.out.println(t);
}
public static void main(String[] args) {
out("findingsea");
out(123);
out(11.11);
out(true);
}
}
再看一个泛型方法和可变参数的例子:
public class Main {
public static <T> void out(T... args) {
for (T t : args) {
System.out.println(t);
}
}
public static void main(String[] args) {
out("findingsea", 123, 11.11, true);
}
}