集合框架
泛型
为什么需要使用泛型:
1.存储在任意类型的数据在集合中,取出来都是Object类型的,此时就要强制转换
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
Object ele = list.get(0);
Integer num = (Integer) ele;
System.out.println(num);
}
2.约束储存到集合中的元素必须是相同的数据类型(相同数据类型才能作比较,比如TreeSet类)
TreeSet set = new TreeSet();
set.add(123);
set.add("ABC");//报错:ClassCastException
3.设计一个点类(point),来封装坐标位置,要求坐标支持String类型,Integer类型和Double类型
如果不使用泛型,将像下面这样定义:
class Point1{
private String x;
private String y;
}
class Point2{
private Integer x;
private Integer y;
}
class Point3{
private Double x;
private Double y;
}
这样重复了代码,使得代码非常不优雅。
泛型(GenericType):Java5开始支持的新的语法
1.广泛通用的类型;
2.代码模板中类型不确定,谁调用这段代码,就是什么类型;
Point.java
//Type(T);Element(E);Key(K);Value(V)
public class Point <T> {
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
PointDemo.java
public class PointDemo {
public static void main(String[] args) {
//使用String类型
Point<String> p1 = new Point<String>();
String x1 = p1.getX();
//使用Integer类型
Point<Integer> p2 = new Point<Integer>();
Integer x2 = p2.getX();
//使用Double类型
Point<Double> p3 = new Point<Double>();
Double x3 = p3.getX();
}
}
我们使用<>
来传入泛型的类型,如:List<String> list = new List<String>();
前后<>
中的内容必须一样,因此不存在泛型继承的概念。
但是后面的<>
内容可以省略,如List<String> list = new List<>();
我们从反编译代码中可以看出:
其实泛型也是一种语法糖!
泛型方法
情况一:泛型类中的泛型只能适用于非静态方法,如果需要给静态方法设置泛型,此时应该使用泛型方法;
情况二:泛型类中的泛型应该适用与整个类中多个方法,有时只对某一个方法设置泛型即可。
一般的,把自定义的泛型作为该方法的返回类型才有意义,而且此时的泛型必须是由参数设置进来的。如果没有参数来设置泛型的具体类型,此时的方法一般设置为Object类型。
泛型方法具体展示:
public static <T> T doWrok(T val) {
return val;
}
泛型的通配符以及上限和下限
泛型的通配符:
不知道使用什么类型来接收的时候,此时可以使用?
,表示未知。
此时只能接收数据,不能声明和存储数据。
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();//这里不能用<?>来声明
doWork(list1);
List<String> list2 = new ArrayList<>();
doWork(list2);
}
public static void doWork(List<?> list){
}
泛型的上限和下限:用来限定元素的类型必须是X的子类或相同,X的父类或相同。
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<>();
doWork1(list1);//true
doWork1(list2);//false
doWork1(list3);//true
doWork1(list4);//false
doWork2(list1);//false
doWork2(list2);//false
doWork2(list3);//true
doWork2(list4);//true
}
//泛型的上限:此时的泛型?必须是Number类或者是Number类的子类
public static void doWork1(List<? extends Number> list){
}
//泛型的下限:此时的泛型?必须是Number类或者是Number类的父类
public static void doWork2(List<? super Number> list){
}
}
泛型的擦除和转换
泛型的擦除:
1):泛型编译后就消失了;
2)当把带有泛型的集合赋给不带泛型的集合时,此时泛型被擦除(手动擦除);
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(123);
List list2 = null;
list2 = list1;// 此时泛型擦除
list2.add("ABC");
// 带有String类型的泛型
List<String> list3 = null;
list3 = list2;
System.out.println(list3);
//等价于String num2 = 123 报错
String num = list3.get(0);
}
堆污染:
当一个方法既使用泛型,又使用可变参数的时候,此时容易导致堆污染问题。
//Type safety: Potential heap pollution via varargs parameter arr
@SafeVarargs //抑制警告
public static <T>List<T> asList(T... arr){
return null;
}