一、泛型
(官方定义):泛型的出现是JDK 1.5以后,它的本质是参数化类型的应用,可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
我自己的理解是:泛型类似于方法中参数。只是泛型把数据的类型作为参数传递,就是将当前我们需要操作的数据的类型指定为参数,用到的时候,再具体指定他对应的类型。
在项目开发中我没有尝试过完全不使用泛型的情况,在我开始写代码的时候已经在使用泛型了。虽然没有完全掌握泛型的原理,但已经在简单的使用了。写了个简单的小例子感受了一下。(被举了很多次的例子)
import java.util.ArrayList;
/**
* 不使用泛型的例子
*
*/
public class Generic {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList arrayList=new ArrayList();
arrayList.add("caka");
arrayList.add(100);
arrayList.add(2.3f);
for (int i = 0; i < arrayList.size(); i++) {
String temp = (String) arrayList.get(i);
System.out.println(temp);
}
}
}
运行这段代码报java.lang.ClassCastException异常,强制类型转换的时候出错。这里代码的错误写的很明显,arrayList中默认存放的是Object类型的值,现在存放了三种类型int,float,string,是没有任何问题的。但是在获取arrayList中值的时候,我们无法确定取出的值是什么类型,所以会在运行时出现ClassCastException的异常。
import java.util.ArrayList;
/**
* 使用泛型的例子
*
*/
public class Generic {
public static void main(String[] args) {
ArrayList <String> arrayList=new ArrayList <String>();
arrayList.add("caka");
arrayList.add("100");
// arrayList.add(2.3f);
for (int i = 0; i < arrayList.size(); i++) {
String temp = (String) arrayList.get(i);
System.out.println(temp);
}
}
}
上面第一个例子,可以理解成arrayList在将对象add之后,并不能记录每个add后对象的本身类型,编译的时候全部默认为Object类型了,所以在运行的时候无法确定获取到的下一个值是什么类型。但在使用泛型的时候,会在arrayList申明时给定参数,arrayList.add(1); arrayList.add(2.3f);在编译的时候就会报错。
结合泛型定义,在上个例子中, ArrayList <String> arrayList=new ArrayList <String>()里String相当于是类型实参,那应该有相应的类型形参。且get()方法的返回结果也直接是此形参类型对应的类型(即类型实参)
二、泛型的定义和使用
泛型可以用于类、接口和方法上(泛型类、泛型接口和泛型方法)
1.泛型类的定义
public class Generic {
public static void main(String[] args) {
Colleage<String> colleage=new Colleage<String>("undergraduate");
String studentName=colleage.getStudent();
System.out.println(studentName);
colleage.setStudent("postgraduate");
studentName=colleage.getStudent();
System.out.println(studentName);
}
}
//泛型类的定义
class Colleage<T>{
private T student;
public Colleage(T student){
this.student=student;
}
public T getStudent() {
return student;
}
public void setStudent(T student) {
this.student = student;
}
}
上面的泛型类中,T表示泛型形参,用于接收来自外部使用时候传入的类型实参,上例中的类型实参就为String类型。泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
定义的泛型类不一定必须要传入泛型实参,如果传入实参,就会做类型限制。不传入参数也是被允许的,在泛型类中使用泛型的方法可以是任意类型。比如:
Colleage colleage=new Colleage("undergraduate");
String studentName=colleage.getStudent();
System.out.println(studentName);
之前好奇泛型形参E接受不同传入的泛型实参,生成的对象实例的类型是相同还是不同,测试了一下,发现是相同的。
public class Generic {
public static void main(String[] args) {
Colleage<String> colleage1=new Colleage<String>("undergraduate");
Colleage<Integer> colleage2=new Colleage<Integer>(985);
System.out.println(colleage1.getClass()==colleage2.getClass());
}
}
程序运行结果是true。可见虽然传入了不同的类型实参
,但并没有真正意义上生成不同的类型。传入不同泛型实参的泛型类在内存上只有一个,还是上例中的Colleage类型。(涉及类型擦除)
2.泛型接口的定义(简单举例)
public interface GenericInterface<T,U> {
void getGeneric(T t,U u);
}
实现泛型接口的类没有传入泛型实参的时候,与泛型类的定义相同,在声明类的时候,需要将泛型的声明也一起加到类中。
public class Generic <T,U> implements GenericInterface<T,U> {
void getGeneric(T t,U u){
}
}
泛型接口的实现类传入了确定的泛型实参。相当于可以生产一个GenericInterface接口,传入无数个实参,
public class Generic implements GenericInterface<String, Double> {
@Override
public void getGeneric(String s, Double d) {
System.out.println(s);
System.out.println(d);
}
public static void main(String[] args) {
Generic generic=new Generic();
generic.getGeneric("19960101", 11.11);
}
}
3.泛型方法
public static <T, U> T get(T t, U u) {
if (u != null)
return u;
else
return t;
}
泛型方法在声明的时候要在方法返回类型之前声明类型参数
那如果我们在调用泛型方法的时候,有时候需要限制类型参数的类型范围,比如,比较数字大小的方法类型需要限定为Number或者其子类的实例。即有界参数。
public static <T> T maximum(T x,T y,T z) {
T max=x;
if(y.compareTo(max)>0){//1
max=y;
}
if(z.compareTo(max)>0){//2
max=z;
}
return max;
}
上面的方法会在1和2处编译错误。编译之前,还在定义这个泛型方法的时候,泛型类型T的类型不能确定。所以,只能默认T为原始类型Object。所以它只能调用来自于Object的那几个方法,而不能调用compareTo方法。类型限定就很有必要了。由于compareTo方法的使用需要实现Comparable接口。所有该泛型方法需要做如下修改:
public static <T extends Comparable<T>> T maximum(T x,T y,T z) {
T max=x;
if(y.compareTo(max)>0){
max=y;
}
if(z.compareTo(max)>0){
max=z;
}
return max;
}
需要注意的是:
1、不管是限定类还是接口,统一都使用关键字 extends
2、有多个限定需要使用&符号给出多个限定。
public static <T extends Comparable&Serializable> T maximum(T x,T y,T z)
3.如果限定既有接口也有类,那么类必须只有一个,并且放在首位置
public static <T extends Object&Comparable&Serializable> T maximum(T x,T y,T z)