一、概述
泛型是Java 1.5引入的新特性。泛型的本质是参数化类型,这种参数类型可以用在类、变量、接口和方法的创建中,分别称为泛型类、泛型变量、泛型接口、泛型方法。将集合声明参数化以及使用JDK提供的泛型和泛型方法是相对简单的,而编写自己的泛型类型会比较困难,但是还是值得思考与学习如何去编写。泛型提供了编译时类型安全检测机制,该机制允许程序员在**编译时检测**到非法的类型。
java 中泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
二、泛型的优势
- 提高代码的安全性和表述性
- 提高代码的重用率
- 泛型机制只在程序编译阶段起作用,只是给编译器参考的
- 使用泛型的好处:集合中存储的元素类型统一了。
从集合中取出的元素类型是泛型指定的类型,不需要进行大量的向下转型!
三、泛型机制的代码实现
注意:Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。
所谓的泛型擦除:
- Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
import java.util.*;
public class test{
public static void main(String args[]) {
//用泛型来指定集合中存储的数据类型
List<Anmial> generic = new ArrayList<>();
//使用泛型之后List集合只能允许存储Anmial类型的数据了,
//generic.add(123);
cat c= new cat();
Birld b = new Birld();
generic.add(c);
generic.add(b);
//获取迭代器,这个表示迭代器迭代的是Anmial类型
Iterator<Anmial> it = generic.iterator();
while (it.hasNext()){
//使用泛型之后,每一次迭代返回的数据都是Anmail类型
Anmial a = it.next();
a.run(); //动物在跑。
//猫在睡觉。
//调用子类型特有的方法还是需要向下转型的!
if (a instanceof cat){
cat x = (cat)a;
x.sleep(); //动物在跑。
} else if (a instanceof Birld) {
Birld y = (Birld)a;
y.fly(); //鸟儿在飞。
}
}
}
}
class Anmial{
public void run(){
System.out.println("动物在跑。");
}
}
class cat extends Anmial{
public void sleep(){
System.out.println("猫在睡觉。");
}
}
class Birld extends Anmial{
public void fly(){
System.out.println("鸟儿在飞。");
}
}
四、JDk8以后引入了:自动类型推断机制
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class test{
public static void main(String args[]) {
//JDk8以后引入了:自动类型推断机制。(又称为钻石表达式)
//ArrayList<这里的类型会自动推断>(),前提是JDK8之后才允许
List<Anmial> myList = new ArrayList<>();
myList.add(new cat());
List<String> l2 = new ArrayList<>();
l2.add("http:www.baidu.com");
l2.add("http:www.QQ.com");
Iterator<String> it = l2.iterator();
while (it.hasNext()){
//直接通过迭代器获取了String类型的数据
String s = it.next();
//直接可以调用String类的substring方法截取字符串。
//如果没有通过迭代器获取String类,则不能直接调用substring的方法
System.out.println(s.substring(5));
}
}
}
五、自定义泛型
//自定义泛型时,<>中是一个标识符,随便写
public class test<T>{
public void doSome(T o){
System.out.println(o);
}
public static void main(String args[]) {
test<String> s = new test<>();
s.doSome("abc");//abc
//类型不匹配
//s.doSome(12345);
Anmial<Integer> i = new Anmial<>();
i.run(123);//123
Anmial<String> a = new Anmial<>();
//返回一个String类型的数据
String ss = a.get();
System.out.println(ss);//null
}
}
class Anmial<E>{
public void run(E w){
System.out.println(w);
}
public E get(){
return null;
}
}
六、泛型机制在什么时候使用?
通常是在集合框架中使用,遇到< >就要定义泛型。
- 其实<>就是用来接收类型的,在使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
七、<? extends T>和<? super T>的区别
分两种情况:
①作为容器时
<? extends T>
没有意义,只能存null
<? super T>
可以放它本身和其子类类型,可以存null
②作为方法参数时
<? extends T>
支持其本身类型及其子类类型,也就是设置上限。
<? super T>
支持本身类型及其父类类型,也就是设置下限。
八、泛型的使用限制
1. 无法使用基本数据类型
2. 无法创建泛型参数类型的实例
public <E> void test(E e) {
E e1 = new E(); // 编译时错误
}
但是可以通过反射来创建对象 :
public <E> void test(E e) {
E e1 = e.getClass().newInstance();// 正确
}
3. 不能为static
字段(属性)声明为泛型类型
4. 无法创建泛型类型的数组
5. 无法创建、捕获或者抛出泛型类型异常
6. 泛型擦除到原生类型的方法无法重载