为什么要使用泛型?
泛型类:
解决参数转换的问题
在不泛型的时候。我们定义一个Point类,如果无法确定成员变量的类型。我们会像下面这样写
class Point{ private Object x; private Object y; public Point(Object x, Object y){ this.x = x; this.y = y; } }当我们使用的时候,可以用一下方式创建一个Point对象。
Point p0 = new Point(5, 6); Point p1 = new Point("115.3'C", 60); Point p2 = new Point(5.4f, 6);
第一个是正确的,但是其他的两个参数类型不一样,所以使用的时候会出很多问题。这个时候就需要用到泛型。
泛型在使用的时候才指定参数类型。有了泛型,代码就可以像下面这样写:
class Point<T>{ private T x; private T y; public Point(T x, T y){ this.x = x; this.y = y; } }
使用的时候只需要指定一下类型。x y类型就统一了,也不会出现类型转换错误。
Point<Integer> p = new Point<>(5, 6);这样,x和y就只能是整型了。
当规定为整形而参数列表中使用了其他类型时,就会在编译阶段就爆出错误。
泛型方法:
泛型不仅可以用来定义类,还可以定义方法。如下定义:
public <T> void fun(T arg){ System.out.println(arg); }
泛型类和泛型方法不冲突:
方式一:
class Point<T>{ private T x; private T y; public Point(T x, T y){ this.x = x; this.y = y; } public <V> V void fun(V arg) { System.out.println(arg); } }
方式二:
class Point<T>{ private T x; private T y; public Point(T x, T y){ this.x = x; this.y = y; } public <T> T void fun(T arg) { System.out.println(arg); } }两种方式都是可以的,互不冲突,但是避免混淆,应该使用方式1.
通配符:
泛型一经过定义就限制了参数的类型。
这样有好处也有坏处。
例如下面的代码会出错:
Point<String> p = new Point<>(5, 6);或者:
Caculate<Integer> caculate = new Caculate<>(); caculate.add(5, 6);
对于一个计算类来说,是不可能只有整形的。我们需要是所有的Number类型都可以被计算。
通配符就是用来解决上述的缺点的。
通配符有三种:
? : 匹配所有类型
? extends String :匹配以String及其子类。由于String是final类,所以String只能匹配String。
? super String :匹配以String及其父类。String类直接继承Object类,所以只能匹配String和Object类
例子:? : 匹配所有类型
class Message<T>{ private T t; public Message(T t) { this.t = t; } public void setT(T t) { this.t= t; } public void print() { System.out.println(this.t); } } public class Test { public static void main(String[] args) { Message<String> m = new Message("4545"); fun(m); Message<Integer> m1 = new Message(5); fun(m1); Message<Double> m2 = new Message(5.5); fun(m2); } public static void fun(Message<?> m) { // m.setT("1234"); //写这句会出错,通配符为?时无法修改使用了通配符属性的值 m.print(); } }
例子:?extends 上限类
class Message<T>{ private T t; public Message(T t) { this.t = t; } public void setT(T t) { this.t= t; } public void print() { System.out.println(this.t); } } public class Test { public static void main(String[] args) { //出错,Number的子类为Integer Byte Double Float Character Short // Message<String> m = new Message("4545"); // fun(m); //这两句会出错,因为<? super String>只能匹配String和Object // Message<Object> m1 = new Message<Object>(new Object()); // fun(m1); Message<Double> m2 = new Message(5.5); fun(m2); Message<Double> m3 = new Message('c'); fun(m3); } public static void fun(Message<? extends Number> m) { // m.setT(8); //写这句会出错,通配符为?时无法修改使用了通配符属性的值 m.print(); } }
例子:?super 下限类
class Message<T>{ private T t; public Message(T t) { this.t = t; } public void setT(T t) { this.t= t; } public void print() { System.out.println(this.t); } } public class Test { public static void main(String[] args) { Message<String> m = new Message("4545"); fun(m); Message<Object> m1 = new Message<Object>(new Object()); fun(m1); } public static void fun(Message<? super String> m) { m.setT("5"); //写这句不会出错,通配符为<? super 下限类>时可以修改使用了通配符属性的值 m.print(); } }
类型擦除:
类型擦除是指,泛型只存在于代码编译阶段,在进入JVM之前,与泛型相关的类型会被替换成相应的类型,对于虚拟机来说,泛型类和普通类没有任何区别。
如果未设置泛型上限。
class Point<T>{ T x; T y; }
会变成
class Point{ Object x; Object y; }
设置了泛型上限就会被替换成相应的泛型上限。