目录
泛型的引入
我们来看一个现象 如果我们想实现一对坐标x,y,能让这个x和y接收不同类型的数据,比如
x=10,y=10,x=10.1,y=11.1,x=”东经15度”,y=北纬39度",我们可以想到用Object来接收
class Point{ private Object x;//用Object来接收不同的数据类型 private Object y; public Object getX() { return x; } public Object getY() { return y; } public void setX(Object x) { this.x = x; } public void setY(Object y) { this.y = y; } }
我们发现如果数据对应正确,那没什么问题,那如果用户输入时,想要让用户输入int类型,他输成double类型,我们程序是中是让披着Object的Integer类型向下转换为Integer,但是用户导致是披着Obbject的Double向下转换为Integer就会发生类型转换错误
但是我们发现这种错误是运行时异常,一般我们要将错误提取暴露在编译阶段,就是程序还没有跑起来就报出错误
泛型类
class 类名 <T>{ T x; } class 类名 <T,E>{ T x; E y; }
- 泛型类可以存在多个类型参数,成员变量的类型不一定一致
- 泛型不能接收八大基本类型,只能接收它的包装类
- T,E这种大写用来表示参数类型,可以使用任何字符,但是大写字母是规范
- T表示任意类型 K表示键值对 V表示value值 E表示单个元素
- 类的泛型就是在定义时没有确定参数类型,使用时明确类型
- 守门员,在编译的阶段检查了类型是否一致
泛型类的使用
类名称 <具体的类> 对象名=new 类名称<>();
使用泛型之后的Point类
class Point<T>{ private T x;//用Object来接收不同的数据类型 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; } }
泛型方法
普通类也可以定义泛型方法
- <T>表示这个方法是泛型方法
- T表示该函数的返回值是T类型
- T t表示方法的参数是T类型
泛型类定义一个泛型方法
- 因为泛型方法的泛型类型与传入泛型方法的类型为准,与泛型类的泛型类型无关,所以在定义的时候将泛型类和泛型方法的类型参数写成不一样的,防止操作异议
- 注意要区分普通方法使用泛型和泛型方法的区别
泛型接口
泛型还可以使用在接口中,一旦一个接口使用泛型,子类实现接口会存在两种选择
- 子类继续保留泛型
- 子类明确当前接口的泛型
出现的问题
- 我们使用多态来实现方法参数传入的多样性,用父类来接收所有子类的对象,但是我们发现msg1传入不进去,因为msg1确定的类型是Interger,但是我们父类确定的是String,所以传入不进去
- 而且不能使用重载接近问题
- 所以引入了通配符
通配符
1 ?通配符,只能定义在方法中,表示可以接收所有类型的泛型类
package generic_paradigm; public class MessageImpl1<T> implements IMessage<T>{ private T msg; @Override public void print(T t) { System.out.println(t); } @Override public T getMsg() { return msg; } @Override public void setMsg(T t) { this.msg=t; } public static void main(String[] args) { IMessage<String> msg=new MessageImpl1(); msg.setMsg("帅帅帅"); IMessage<Integer> msg1=new MessageImpl1(); msg1.setMsg(1111); fun(msg); fun(msg1); } public static void fun(IMessage<?> msg){//用父类来接收子类 System.out.println(msg.getMsg()); } }
- 用?通配符就可以对应在泛型类中不确定类型时,这个泛型类定义不同泛型对应的类型的对象,在方法中接收各种类型对象
- 不能在fun方法中调用修改类型的方法:因为我们在定义fun方法的时候,根本不知道会传入什么类型的对象,我们就不能知道怎么设置值(因为不知道类型),所引只能调用接收方法,不能调用修改的方法
2 ?entends 类 设置泛型类的上限
- 可以使用在类上或者方法上,明确此时泛型的上限是什么
- 可以理解为天花板,我们只知道传入对象的父类是谁,但是我们不知道具体传入的子类
- 不可以调用设置值的方法,因为我们只知道父类是什么 ,不知道其传入的子类,也不能进行向下转型,因为向下转型的条件是它本身就是这个子类,只是披着父类的皮
3 ? super 类 规定泛型的下限
- super只能用于方法,不能用于类型
- 可以理解为地板,我们不知道其传入的到底什么类型的对象,但是肯定是String和String的父类
- 可以在这个方法使用修改的方法,因为我们知道了这个传入的对象肯定是其String或者String的父类,因为向上转型的条件只要是其父类就行,没有其他任何条件,我们传入一个对象,都可以将设置的String值向上转型为其父类
类型擦除
泛型信息其实只存在于编译阶段,进入JVM后,会将所有和泛型相关的信息擦除,泛型类进入JVM和普通类没有任何区别(javac->*.java编译为*.class之后,泛型就不存在了)
- 若没有规定泛型的上限,则所有的泛型信息都会擦除为Object类
- 若规定了泛型的上限,则擦除了相应泛型的上限类型
- 泛型只是Java的语法糖