泛型
1.为什么需要使用泛型
1.存储任意类型的数据在集合中,但是取出来都是Object类型,此时就得强转
2.约束存储到集合中的元素必须是相同的数据类型(相同的数据类型才能做比较,如TreeSet类)
3.设计一个点(point)类,来封装坐标位置,要求坐标位置支持String类型,Integer类型,Double类型。普通做法是定义三个不同类型的类,这样做太不优雅了
2.定义和使用通用泛型类
1.定义
泛型是从java5开始支持的新语法。
什么是泛型呢?
1.广泛通用的类型
2.代码模板中类型不确定,谁调用该段代码谁就指明类型是什么
泛型类:直接在类/接口上定义的泛型
使用泛型:
1.保证前后类型相同List<String> list = new ArrayList<String>();//该List集合中只能存储String类型的元素
2.因为前后类型相同,所有java7开始,退出泛型的菱形语法<>,即后一个可以不写List<String> list = new ArrayList<>();
3.泛型不存在继承的关系(错误如下)List<Object> list = new ArrayList<String>();
4.从此以后,使用集合都得使用泛型来约束该集合中元素的类型
5.通过反编译发现:泛型其实也是语法糖,底层依旧没有泛型,而且依然使用强转
示例:
/**
* Created by cenyu on 16-11-21.
* 点坐标对象
* 在本类中,T表示一种类型,该类具体类型是什么,由调用者来决定
* 通用几种泛型代指.T:type E:element K:key V:value
* 需要定义的类型用尖括号<>在定义的时候括起来,下面就可以将T当做一个类型来用
*/
public class Ponit<T> {
private T x;
private T y;
public T getX() {
return x;
}
public T getY() {
return y;
}
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
}
public class PointDemo {
public static void main(String[] args) {
//使用String类型,Point类中T全部用String替换
//调用的时候需要在等号做右边同时定义类型
Ponit<String> p = new Ponit<String>();
p.setX("3");
String x1 = p.getX();
System.out.println(x1);
//使用Integer类型
//同上
//使用Double类型
//同上
}
}
3.定义和使用泛型方法
泛型方法:在方法上声明泛型
以下情况使用:
1.泛型类中的泛型只能使用于非静态方法,如果需要给静态方法设置泛型,此时使用泛型方法
2.泛型类中的泛型适用于整个类中多个方法,有时候只对某一个方法设置泛型即可
一般的,把自定义的泛型作为该方法的返回类型才有意义。而且此时的泛型必须是有参数设置进来的。如果没有参数来设置泛型的具体类型,此时的方法一般返回设计为Object即可。
/**
* 测试泛型方法
* Created by cenyu on 16-11-22.
*/
public class GebericAdd {
//定义一个泛型方法,规定返回值为double
public <T extends Number> double add(T t1, T t2){
double sum = 0.0;
sum = t1.doubleValue()+t2.doubleValue();
return sum;
}
public static void test(){
GebericAdd addTest = new GebericAdd();
int num1=3;
int num2=4;
System.out.println(addTest.add(num1,num2));//输入Int型,返回double
float num3=3.0f;
float num4=7.0f;
System.out.println(addTest.add(num3,num4));//输入float型,返回double
}
public static void main(String[] args) {
test();
}
}
4.泛型的通配符和上限下限
泛型的通配符:‘
不知道使用什么类型类接收的时候,此时可以用 ? ,?号表示未知,通配符。此时只能接收数据,不能往集合中存储数据
import java.util.ArrayList;
import java.util.List;
/**
* 测试泛型通配符
* Created by cenyu on 16-11-22.
*/
public class GenericTypeDemo {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
doWork(list1);
List<String> list2 = new ArrayList<>();
doWork(list2);
}
//此处不知道会接收什么类型,用?通配符
private static void doWork(List<?> list){
}
}
上限和下限:
用来限定的元素的类型必须是X类的子类或相同类,X的父类或相同类
import java.util.ArrayList;
import java.util.List;
/**
* 测试泛型上下限
* Created by cenyu on 16-11-22.
*/
public class GenericTypeDemo {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<Number> list3 = new ArrayList<>();
List<Object> list4 = new ArrayList<>();
doWork(list1);
// doWork(list2);//此处报错
doWork(list3);
// doWork(list4);//此处报错
// doWork2(list1);//此处报错
// doWork2(list2);//此处报错
doWork2(list3);
doWork2(list4);
}
//泛型的上限:此时的泛型?,必须是Number类型或Number类的子类
private static void doWork(List<? extends Number> list){
}
//泛型的下限:此时的泛型?,必须是Number类型或Number类的父类
private static void doWork2(List<? super Number> list){
}
}
5.泛型的删除和转换
泛型的擦除:
1.泛型编译之后就消失了(泛型自动擦除);
2.当把带有泛型的集合赋给不带泛型的集合,此持泛型被擦除(手动擦除)
示例:
import java.util.ArrayList;
import java.util.List;
/**
* 测试泛型擦除
*/
public class GenericTypeDemo {
public static void main(String[] args) {
//带有Integer类型的泛型
List<Integer> list1 = new ArrayList<>();
list1.add(123);
//不带泛型的集合
List list2 = null;
list2 = list1 //此时泛型被擦除
list2.add("ABC"); //如果没被擦除是不允许用String的
//带有String类型的泛型
List<String> list3 = null;
list3 = list2;//泛型被擦除了,然后又被赋值给list3.
//此时编译器不会报错,但是运行的时候还会报错
//其实是等价于:String list3 = 123
//此时用list3.get(0),是会报错的
}
}
堆污染:
上面的代码中,会有报错就是因为对污染。
单一一个方法中既使用泛型的时候也使用可变参数,此时容易导致堆污染问题