爱了! 阿里技术官甩出的内部 Java 泛型笔记,技术人成长的标杆!

363 阅读3分钟

Hello,今天给各位童鞋们分享Java泛型,赶紧拿出小本子记下来吧!

image.png

一、什么是泛型

1.背景:

JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。如下面的例子

public class demo {

public static void main(String[] args) {

    ArrayList list = new ArrayList();

    list.add("java");

    list.add(132);

    list.add(true);

    for (int i = 0; i < list.size(); i++) {

         Object o = list.get(i);

         String str = (String)o;

         System.out.println(str);

    }

}

}

运行后会报错

image.png 错信息为数据类型转化异常,Integer不能被转化为String类型,这个不是编译过程的中的错误,是代码运行时候的错误。

2.概念:

Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

3.好处:

编译期间检查类型

消除了强制类型的转换

如图

image.png 输入int和boolean类型都会报错

ArrayList list1 = new ArrayList<>();

    list1.add("a");

    list1.add("b");

    list1.add("c");

    for (int i = 0; i < list1.size(); i++) {

        String str = list1.get(i);

        System.out.println(str);

    }

运行结果为

image.png 消除了强制类型转化

二、泛型类、接口

1.泛型类

(1)使用语法

类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();

(2)Java1.7以后,后面的<>中的具体的数据类型可以省略不写

类名<具体的数据类型> 对象名 = new 类名<>(); 菱形语法

2.泛型类注意事项:

泛型类,如果没有指定具体的数据类型,此时,操作类型是Object。

泛型的类型参数只能是类类型,不能是基本数据类型,因为在编译的时候,类都会被视为Object类去进行编译,基本数据类型没有继承自Object所以不能用基本数据类型。

泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型

可以结合后面的泛型擦除理解,泛型擦除之后 Generic < String > 和Generic< Integer >都是Generic类。

泛型类:

image.png 测试类:

image.png 打印结果

image.png

3.从泛型类派生子类

  • 子类也是泛型类,子类和父类的泛型类型要一致

image.png

  • 子类不是泛型类,父类要指定泛型类型

image.png

4.泛型接口

泛型接口的定义语法

interface 接口名称 <泛型标识,泛型标识,…> {

泛型标识 方法名(); 

.....

}

5.泛型接口的使用

  • 实现类也是泛型类,实现类和接口的泛型类型要一致

image.png 实现类不是泛型类,接口要明确数据类型

/**

  • 实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型。

*/

public class Apple implements Generator {

@Override

public String getKey() {

    return "hello generic";

}

}

三、泛型方法

1.用法

泛型方法是在调用方法的时候指明泛型的具体类型。

2.语法:

//修饰符 <T,E, ...> 返回值类型 方法名(形参列表) { 方法体... }

public <T> void Getkey(ArrayList<T> key_list){

   

}

3.说明:

public与返回值中间非常重要,可以理解为声明此方法为泛型方法。

只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。

< T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。

与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

4.泛型方法与可变参数

public class Test {

public <E> void print(E... e){

    for (int i=0;i<e.length;i++){

        System.out.println(e[i]);

    }

}

}

public class demo {

public static void main(String[] args) {

    Test test = new Test();

    test.print(1,2,3);

    test.print("a","b","c");

}

}

打印结果

image.png

5.泛型方法总结

  • 泛型方法能使方法独立于类而产生变化

  • 如果static方法要使用泛型能力,就必须使其成为泛型方法

/**

 * 静态的泛型方法,采用多个泛型类型

 * @param t

 * @param e

 * @param k

 * @param <T>

 * @param <E>

 * @param <K>

 */

public static <T,E,K> void printType(T t, E e, K k) {

    System.out.println(t + "\t" + t.getClass().getSimpleName());

    System.out.println(e + "\t" + e.getClass().getSimpleName());

    System.out.println(k + "\t" + k.getClass().getSimpleName());

}

四、类型通配符

1.什么是类型通配符

类型通配符一般是使用"?"代替具体的类型实参。

public static void showBox(Test<?> test){

    System.out.println(test.getKey());

}

所以,类型通配符是类型实参,而不是类型形参。

2.类型通配符的上限

语法:

类/接口<? extends 实参类型>

public static void showBox(Test<? extends Number> test){

    System.out.println(test.getKey());

}

要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

3.类型通配符的下限

语法:

//类/接口<? super 实参类型>

public static void showBox(Test<? super Integer> test){

    System.out.println(test.getKey());

}

要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

五、类型擦除

1.概念

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。

2.分类:

  • 无限制类型擦除

image.png

  • 有限制类型擦除

image.png

  • 擦除方法中类型定义的参数

image.png

  • 桥接方法

image.png

六、泛型与数组

  • 泛型数组的创建可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象

//错误方式

ArrayList[] listArr = new ArrayList<5>();

//正确方式

ArrayList[] list = new ArrayList[5];

ArrayList[] listArr = list;

//或者

ArrayList[] listArr = new ArrayList[5];

  • 可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组

public class Fruit {

private T[] array;

public Fruit(Class<T> clz, int length){

    //通过Array.newInstance创建泛型数组

    array = (T[])Array.newInstance(clz, length);

}

}

好啦,今天的文章就到这里,希望能帮助到屏幕前迷茫的你们