不引入泛型的问题
当将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
因此取出集合元素时需要人为的强制类型转化到具体的目标类型,但是很容易出现ClassCastException异常
那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现java.lang.ClassCastException异常呢?
答案就是使用泛型,可以将运行时的类型检查搬到编译期实现
什么是泛型?
泛型是jdk5引入的类型机制,就是将类型参数化。泛型作为一种安全机制而产生
泛型机制将类型转换时的类型检查从运行时提前到了编译时,使用泛型编写的代码比杂乱的使用object并在需要时再强制类型转换的机制具有更好的可读性和安全性。
泛型在本质上是指类型参数化。所谓类型参数化,是指用来声明数据的类型本身,也是可以改变的,它由实际参数来决定。在一般情况下,实际参数决定了形式参数的值。而类型参数化,则是实际参数的类型决定了形式参数的类型。
在声明List<E>阶段E是什么类型不确定,这里的E仅仅充当占位符的作用,在具体调用时类型才能确定,而E的所有位置将被指定的类型所替代
泛型的定义
public interface List<E> extends Collection<E>
这里的<>中的内容就是类型参数,一般建议使用T或者E之类的全大写
{
E get(int index); 获取的元素类型就是定义时指定的类型
void add(E element);
}
List<String> list=new ArrayList<String>();就是将String传递给E,用于替代定义中的E
String str=list.get(0); 不需要进行类型转换
List<Date> list=new ArrayList<>();//这里使用菱形语法,支持泛型推导
list.add("abc");//语法报错,编译时就会进行类型检查
list.add(123);//语法报错
list.add(new Date());
for(int i=0;i<list.size();i++){
Date temp=list.get(i); 直接获取目标类型,不需要进行类型转换
System.out.println(temp.getYear()+1900);
}
典型场景
获取两个整数中较大的整数
Integer max(Integer a, Integer b){
return a>b?a:b;
}
如果需要比较的不是Integer类型,而是Double或是Float类型,那么就需要另外再写max()方法【方法的重载】。
引入泛型的目的实际上就是能够在编写max()方法时,不必确定参数a和b的数据类型,而等到调用的时候再来确定这两个参数的数据类型,那么只需要编写一个max()就可以了,这将大大降低程序员编程的工作量。Comparable接口---用于类定义中表示当前类型的对象是可比较的,接口中定义了比较方法,要求具体类型提供实现【具体的比较规则】
Java中有一个系统预定义的接口
public interface Comparable<T> { 用于实现规范类的可比较性
int compareTo(T o); 含义是使用当前对象和参数o对象进行比较
当前类型比较时需要返回一个整数,当当前对象大于参数o时,返回一个正数;如果等于时则返回0;如果小于时返回一个负数
}
在定义中可以参数Integer类中的方法实现
public final class Integer 最终类表示Integer不能被继承
extends Number 所有的系统预定义的数值类型包装类都继承与Number
implements Comparable<Integer> 实现了可比较的接口,当前类不是抽象类,所以必须提供compareTo方法的实现
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y){
return (x<y)?-1:((x==y)?0:1);
}
针对获取两个对象之间最大的对象的方法
public class MyTest {
public static <T extends Comparable<T>> T max(T t1, T t2) {
return t1.compareTo(t2) > 0 ? t1 : t2;
}
}
import java.util.Date;
/*
* 所谓泛型类generic class就是具有一个或多个类型参数的类
*/
public class Test1 {
public static void main(String[] args) {
// A1<Object> aa2=new A1<String>();
A1<String> aa1=new A1<>();
aa1.name="zhangsan";
// aa1.name=1234; //可以在编译期进行类型检查
A1<Date> aa2=new A1<>();
aa2.setName(new Date());
System.out.println(aa2.getName().getYear()+1900);
@SuppressWarnings("rawtypes")
A1 aa3=new A1();
aa3.setName("nihao");
aa3.setName(123456);
System.out.println(aa3.getName());
}
}
class A1<T>{ // 在当前类的成员定义中,可以将T当作类型使用
T name;
public T getName() {
return this.name;
}
public void setName(T name) {
this.name = name;
}
}
如果定义了泛型类,但是引用时不声明泛型值,系统则识别泛型为Object类型
A1 aa=new A1(); 不会报错,但是有警告信息,不建议这种用法
aa.setName("abc"); 正确
aa.setName(123); 正确
name可以传入任意类型的数据,因为系统识别 name为Object类型
class A1中T是类型参数的名称。在创建一个对象时,这个类型名称用作传递给A1的实际类型的占位符。
因此在A1中,每当需要类型参数时,就会用到T。注意T是被括在<>中的。每个被声明的类型参数,都要放在尖括号中。由于Generic使用了类型参数,所以它是一个泛型类,也被称为参数化类型。
在A1类中使用T定义了一个属性T name。由于T只是一个占位符,所以name的实际类型要由创建对象时的参数传递进来。
T事实是一个数据类型的说明,它可以用来说明任何实例方法中的局部变量、类的成员变量、方法的形式
参数以及方法的返回值。但是类型参数T不能使用在静态方法中。public class Generic<T> {
private static T name;//语法报错
public static T getName(){//语法报错
return name;
}
}最后还需要注意:声明一个泛型实例时,传递给形参的实参必须是类类型,而不能使用int或char之类的简单类型。如果不传递类型,则系统默认类型为Object
带两个类型参数的泛型类
如果引用多个类型,可以使用逗号分隔:<S, D>
类型参数名可以使用任意字符串,建议使用有代表意义的单个字符,以便于和普通类型名区分,如:T代表type,有原数据和目的数据就用S/D,子元素类型用E等。当然,你也可以定义为XYZ,甚至xyZ。
泛型是JDK1.5的所有新特性中最难深入掌握的部分,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中List list=new ArrayList();
list.add("123");
list.add(456);
list.add(new Date());
因为系统识别的类型为Object使用泛型集合,可以将一个集合中的元素限定为一个特定类型,这样集合中就只能存储同一类型的对象,这样更安全;并且当从集合中获取一个对象时,编译器也知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。List<String>list=new ArrayList<>(); 第2个类型可以不写,JDK1.7引入的泛型推导
list.add(“123”);正确
list.add(456);错误,类型不合法在JDK1.5之后,你还可以按原来的方式将各种不同类型的数据放到同一个集合中,但是编译时会报一个unChecked警告
可以使用注解避免警告信息
@SuppressWarnings("rawtypes")
A1 aa4=new A1();
去除所有的警告信息 @SuppressWarnings("all")泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,
记住:Collection<String>和Collectin<Object>是两个没有转换关系的参数化的类型
集合类中定义范型
List<String> list = new ArrayList<String>();
Map<String,Object> map = …
List<Map<String,Object>> list …范型只能使用引用类型,而不能使用基本类型,即:`List<int>`是错误的
最佳实践:保持良好的编程风格,尽量使用范型