为什么要使用泛型?
泛型类:
解决参数转换的问题
在不泛型的时候。我们定义一个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;
}
设置了泛型上限就会被替换成相应的泛型上限。