逸学java【初级菜鸟篇】9.4 泛型

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。

我们反编译看看

我们在一开始检查以后,如果类型不匹配编译器就会直接报错 ,如果匹配,编译类型擦除,如果又需要什么操作,又找回信息,进行匹配(会强制转换),也就是在泛型信息被擦除后,若还需要使用到对象相关的泛型信息,编译器底层会自动进行类型转换(从原始类型转换为未擦除前的数据类型)。

猜你喜欢

转载自blog.csdn.net/m0_59337285/article/details/134560850