泛型
一、引例
如创建个人信息类:包含String name、int age、double sorce;由于类型涵盖比较多我们不由会想起用Object类型来保存所有的类型。
- class Information{
- private Object name;
- private Object age;
- private Object sorce;
- public Object getName() {
- return name;
- }
- public void setName(Object name) {
- this.name = name;
- }
- public Object getAge() {
- return age;
- }
- public void setAge(Object age) {
- this.age = age;
- }
- public Object getSorce() {
- return sorce;
- }
- public void setSorce(Object sorce) {
- this.sorce = sorce;
- }
- }
- public class genericity4_24 {
- public static void main(String[] args) {
- Information info = new Information();
- //自动转型并向上转型为Object
- info.setName("lemon");
- info.setAge("20");
- info.setSorce(88);
- int age = (int)info.getAge();//强制向下转型为int
- System.out.println(age);
- }
- }
在上述例子中我们明显可以看出正式由于设置为Object类型,由于设置方设置了错误的类型数据但接收方并不知情所以就会出现ClassCastExceptoion(两个没有关系的对象进行强制类型转换出现的异常)错误。
而在此时编译阶段并没有报错,而是在运行时出现了此类错误,则我们可以得知向下转型是不安全的操作,会带来一些隐患。
二、泛型
为了解决上述问题我们接下来引入泛型:
泛型是在jdk1.5引入的,泛型其实指得就是参数化类型,使得代码可以适应多种类型。
1、泛型类
泛型类指的是在类定义时不会设置类中属性和方法中参数的具体类型,而在类使用时再进行定义。
基本语法
- // 其中 <>中的T被称为类型参数,可用来代指任意类型;
- //T可任意写,但出于规范,一般用单个大写字母来代表类型参数.
- //T 代表一般的任何类。
- //E 代表 Element 的意思,或者 Exception 异常的意思。
- //K 代表 Key 的意思
- //V 代表 Value 的意思,通常与 K 一起配合使用。
- //S 代表 Subtype 的意思
- class Information <T>{
- private T name;
- }
若一个类被<T>定义,则称其为泛型类.
(1)泛型类的使用
1.泛型类中只包含一个类型参数
- /*
- * 泛型类中只包含一个类型参数
- * */
- class Information <T>{
- //T可为任意类型,在实例化时给出
- private T name;
-
- public T getName() {
- return name;
- }
- //setting方法中,形式参数类型仍然为T,具体类型以实例化为准
- public void setName(T name) {
- this.name = name;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- //形式化参数为String类型
- //在jdk1.7以后,下述语句可以改写为Information info = new Information <String,Integer>();
- Information <String> info = new Information <String>();
- info.setName("lemon");
- //取出数据时避免了向下转型
- System.out.println(info.getName());
- }
- }
2.泛型类中包含多个类型参数
- /*
- * 泛型类中包含多个类型参数
- * */
- //在泛型类中定义多个类型参数,T,E均表示一个占位符,若有更多的参数继续在其后追加
- class Information <T,E>{
- //T,E可为任意类型,在实例化时给出
- private T name;
- private E age;
- public T getName() {
- return name;
- }
- public E getAge() {
- return age;
- }
- //setter方法中,形式参数类型仍然为T,E,具体类型以实例化为准
- public void setName(T name) {
- this.name = name;
- }
- public void setAge(E age) {
- this.age = age;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- //形式化参数为多个,在<>用,隔开
- //且泛型只能接受类,所有基本数据类型都要使用其包装类
- Information <String,Integer> info = new Information <String,Integer>();
- info.setName("lemon");
- info.setAge(20);
- System.out.println(info.getName());
- System.out.println(info.getAge());
- }
- }
当传入参数错误时就会在编译阶段报错,消除了安全隐患。
由此我们可以得出泛型的引入彻底改变了向下转型的需求。引入泛型后,若明确设置了类型,则为设置类型;若没有设置类型,则默认为Object类型。
2、泛型方法
泛型不仅可以在类中定义同时也可在方法中定义。
(1)泛型方法的基本语法
- class Information<E>{
- private E name;
- private E classBelongs;
- //泛型类中定义泛型方法<>中的E被称为类型参数,而方法中的T被称为参数化类型,不是运行时真正的参数
- public <T> void printInformation(T name,T classBelong) {
- System.out.println("姓名:"+this.name+",班级:"+this.classBelongs);
- }
- }
(2)泛型方法的使用
泛型方法与泛型类有所不同,泛型类在类名之后加<E>,而泛型方法是在返回值之前加<E>.
泛型方法始终以自己定义的类型参数为准。且声明的类型参数,也可以当作返回值的类型来使用。
- /*
- * 泛型类中定义泛型方法和普通方法
- * */
- //泛型类
- class Information<E>{
- private E name;
- private E classBelongs;
- public E getName() {
- return name;
- }
- public void setName(E name) {
- this.name = name;
- }
- public E getClassBelongs() {
- return classBelongs;
- }
- public void setClassBelongs(E classBelongs) {
- this.classBelongs = classBelongs;
- }
- //泛型类中的泛型方法,泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,形式化参数中的E不是运行时真正的参数
- public <E> void printInformation(E name,E classBelong) {
- System.out.println("姓名"+this.name+",班级:"+this.classBelongs);;
- }
- public <E> E print(E e) {
- return e;
- }
- //泛型类中的普通方法
- public void printInformation1(E name,E classBelong) {
- System.out.println("姓名:"+this.name+",班级:"+this.classBelongs);
- }
- public void print1(E e) {
- System.out.println(e);
- }
-
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Information <String> info = new Information <String>();
- info.setName("lemon");
- info.setClassBelongs("数学152");
- info.printInformation(info.getName(),info.getClassBelongs());
- info.printInformation1(info.getName(),info.getClassBelongs());
- //泛型方法种的参数化类型为整形
- System.out.println(info.print(100));
- //普通方法种的参数化类型必须为实例化时所对应的类型,若此时像泛型方法中一样给其赋值为int型,则会出现错误,如下图
- info.print1("100");
- }
- }
结论:
泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,泛型方法始终以自己定义的类型参数为准。
泛型类的实际类型参数是 String,而传递给泛型方法的类型参数是 Integer,两者不相干。一般为了避免混淆,若一个泛型类中存在泛型方法,那么两者的类型参数最好不要同名。
3、泛型接口
泛型除了可以定义在类,方法中外,还可以定义在接口中,我们称之为泛型接口。
泛型接口有两种实现方式:子类实现时继续使用泛型;子类实现接口时给定类型。
- /*
- * 泛型接口
- * */
- //定义泛型接口
- interface IMessage<T>{
- public void print(T t);
- }
- //子类实现接口继续使用泛型
- class MessageImpl <T> implements IMessage<T>{
- @Override
- public void print(T t) {
- System.out.println(t);
- }
- }
- //子类实现接口时明确给定类型
- class MessageImpl1 implements IMessage<String>{
-
- @Override
- public void print(String t) {
- System.out.println(t);
- }
-
- }
- public class genericity4_24{
- public static void main(String[] args) {
- //向上转型
- IMessage <String> message = new MessageImpl();
- message.print("hello");
- IMessage <String> message2 = new MessageImpl1();
- message2.print("world");
- }
- }
4、通配符
(1)通配符的引入
- class Message<T>{
- private T message;
- public T getMessage() {
- return message;
- }
- public void setMessage(T message) {
- this.message = message;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Message message = new Message<String>();
- message.setMessage("Hello World");
- //若将以上语句更改为 message.setMessage(99) 即泛型的类型由String设置为Integer则会出现如下图所示的错误信息
- fun(message);
- }
- public static void fun(Message<String> t) {
- System.out.println(t.getMessage());
- }
- }
(2)通配符的使用
由上述情况得出,不能用来接受任意类型,所以我们要对其进行扩展使其可以接受任意类型。由此就有了通配符的概念:
- class Message<T>{
- private T message;
- public T getMessage() {
- return message;
- }
- public void setMessage(T message) {
- this.message = message;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Message message = new Message<String>();
- //若将以上语句更改为 message.setMessage(99) 则会出现如下图所示的错误信息
- message.setMessage(99);
- fun(message);
- }
- //此时使用通配符?描述的是它可以接受任意类型,
- //但是由于类型是不确定的所以无法对内容进行修改
- public static void fun(Message <?> t) {
- //t.setMessage("ooo");
- System.out.println(t.getMessage());
- }
- }
-
(3)通配符的扩展
在通配符即基础上又可扩展出两个子通配符:
a. 设置泛型上限 _extends 类
b. 设置泛型下限 _super 类
例如:
? extends Number,表示只能够设置Number或其子类,例如:Integer、Double等;
? super String,表示只能够设置String及其父类Object。
- //设置泛型上限,则实例化时只能实例化为Number及其子类
- class Message<T extends Object>{
- private T message;
- public T getMessage() {
- return message;
- }
- public void setMessage(T message) {
- this.message = message;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Message message = new Message<Number>();
- message.setMessage(99);
- System.out.println("我为设置泛型上限的测试用例");
- fun(message);
- System.out.println("我为设置泛型下限的测试用例");
- fun1(message);
- }
- //此时 ?extends Number 表示可以接受的类型为Number及其子类
- //但还是不确定其具体类型,所以仍然无法对其内容进行修改
- public static void fun(Message <? extends Number> t) {
- //t.setMessage("ooo");
- System.out.println(t.getMessage());
- }
- //此时 ?super Number 表示可以接受的类型为String及其父类
- //但我们可以已经大概知道其类型,可发生自动向上转型所以可以对其内容进行更改
- public static void fun1(Message<? super String> t1) {
- t1.setMessage("hello");
- System.out.println(t1.getMessage());
- }
- }
5、类型擦除
泛型的概念是jdk1.5才引进的,在其之前没有泛型的概念,由此可以看出泛型对之前的版本具有很好的兼容性。
那是因为泛型信息只存在于代码编译阶段,在进入JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。(即在进入JVM之前,泛型类和普通类在本质上没有什么区别)
- class Message<T>{
- private T message;
- public T getMessage() {
- return message;
- }
- public void setMessage(T message) {
- this.message = message;
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Message message = new Message<String>();
- Message message2 = new Message<Object>();
- System.out.println(message.getClass());
- System.out.println(message2.getClass());
- System.out.println(message.getClass()==(message2.getClass()));
- }
- }
在JVM中Class 都是class struct.Message所以打印结果是true.
- //T指定泛型上限,E 未指定
- class Message<T extends String,E>{
- private T name;
- private E age;
- public T getName() {
- return name;
- }
- public void setName(T name) {
- this.name = name;
- }
- public E getAge() {
- return age;
- }
- public void setAge(E age) {
- this.age = age;
- }
- public void print(T name) {
- this.name = name;
- }
- public void testMessage(T t) {
- System.out.println(t);
- }
- }
- public class genericity4_24{
- public static void main(String[] args) {
- Message mes = new <String,Integer> Message();
- Class class1 = mes.getClass();
- Field[] fields = class1.getDeclaredFields();
- for (Field field : fields) {
- System.out.println(field.getType());
- }
- }
- }
在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如<E> 则会被转译成普通的Object 类型,T指定了上限T extends String> 则类型参数就被替换成类型上限。