hi,我是逸尘,一起学java吧
泛型概述
泛型是我们在定义某一个类型规格的时候使用的泛指,我们预先定义一个大方向,防止路线错误。
实质上是程序员定义的安全类型,Object是顶级父类,在没有泛型很多程序员为了使程序更通用,设计程序时通常传入值和返回值对设置Object为主,但是必须正确的使用实例,正确转回原来类型,比较不方便,所以java提供了泛型。
泛型标识是任意设置的(如果你想可以设置为YC都行)但是常见还是默认给予一些意义
T :代表一般的任何类。
E :代表 Element 元素的意思,或者 Exception 异常的意思。
K :代表 Key 的意思。
V :代表 Value 的意思,通常与 K 一起配合使用。
S :代表 Subtype 的意思,文章后面部分会讲解示意。
泛型一般格式
类名<T>
我们来看这样的一个情况
public void test() {ArrayList list = new ArrayList();list.add("aaa");list.add("bbb");list.add("ccc");for (int i = 0; i < list.size(); i++) {System.out.println((String)list.get(i));}
}
这代码代码是没有问题的,那么如果说我们将“ccc”改为111
述代码在编译时没有报错,但在运行时却抛出了一个 ClassCastException 异常
,其原因是 Integer 对象不能强转为 String 类型。
那么我们想提前知道错误,所以我们需要一个大方向,这个时候便可以使用泛型
了。
泛型格式还可以声明多个类型
类<T1,T2>
泛型限制
也可以对泛型限制,这个主要是创建类时类型限制
class 类<T extends anyclass>
extends就是继承,表示这个泛型必须继承anyclass这个接口或者类
还提供了一个通配符? 和上面的区别是?它是在创建泛类型对象时限制
”T“ 是一个形参,表示所有String类的派生类其中的 ”某一个类”,当使用的时候会被强转成传入的具体类型,而”?“是一个实参,表示所有String类的派生类的集合,可以理解为一个范围。
泛类型名称<? extends anyclass>
所以当使用泛型对象的时候,需要单独实例化
A<? extends List> a=null;
a=new A<ArrayList>();
a=new A<LinkedList>();
public class GenericType {public static void main(String[] args) { ArrayList<Number> list01 = new ArrayList<Integer>();// 编译错误ArrayList<? extends Number> list02 = new ArrayList<Integer>();// 编译正确}
}
但是我们说了?理解为一个范围,它不可以具体化的,就是它的集合可能是 ArrayList< Integer > 集合,也可能是 ArrayList< Float > 集合,在不确定前不能放入
public class GenericType {public static void main(String[] args) { ArrayList<? extends Number> list = new ArrayList<>();list.add(new Integer(1));// 编译错误list.add(new Float(1.0));// 编译错误}
}
那么除了实例化以外还可以去放在方法的参数上
public void dosomething(A<?extend List> a )
那么它就可以限制dosomething()的方法的参数。
我们还需要注意一点,通配符声明的实例化对象不能加入新的信息。
package com.yd.yc;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class TestOne {public static void main(String[] args) {ArrayList<String> one = new ArrayList<String>();one.add("yc");List<?> two=one;List<?> three=new LinkedList<Integer>();System.out.println(two.get(0));one.set(0,"you");//two.set((0,"your");) 使用通配符的就不能调用改变信息}}
类型擦除
最后我们强调一个问题
泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除
。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段
。
public class GenericType {public static void main(String[] args) { ArrayList<String> arrayString = new ArrayList<String>(); ArrayList<Integer> arrayInteger = new ArrayList<Integer>(); System.out.println(arrayString.getClass() == arrayInteger.getClass());// true}
}
我们会发现,泛类型不同的两个 ArrayList 集合,一个是 ArrayList< String>,只能存储字符串。一个是 ArrayList< Integer>,只能存储整型对象。我们通过 arrayString 对象和 arrayInteger 对象的 getClass() 方法获取它们的类信息并比较,发现结果为true。
我们反编译看看
我们在一开始检查以后,如果类型不匹配编译器就会直接报错 ,如果匹配,编译类型擦除,如果又需要什么操作,又找回信息,进行匹配(会强制转换),也就是
在泛型信息被擦除后,若还需要使用到对象相关的泛型信息,编译器底层会自动进行类型转换
(从原始类型转换为未擦除前的数据类型)。