最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

java泛型和通配符

来源:博客园

java泛型/通配符

泛型

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(表示Java 类,包括基本的类和我们自定义的类)
  • K - Key(表示键,比如Map中的key)
  • V - Value(表示值)
  • N - Number(表示数值类型)
  • ? - (表示不确定的java类型)

为什么会有泛型?

1.需要解决代码冗余,提高复用性

2.需要编译期的检查


(资料图)

编译期检查

ArrayList arrayList = new ArrayList<>();        arrayList.add(dog);        arrayList.add(new Object());//编译器这里会报错

运行结果

java: 对于add(java.lang.Object), 找不到合适的方法

提高代码复用性

DogCat分别继承 Animal

/*   void run(Dog t) {        System.out.println(t);    }    void run(Cat t) {        System.out.println(t);    }    void run(Animal t) {        System.out.println(t);    }*/void run(T t) {    System.out.println(t);}

T可以传入任意类型,和Object一样,但一般都会给泛型一个界限,有助于编译期发现问题

//如果T 指定为Animal类型 则think.run(dog);think.run(cat);think.run(new Car());//编译期会报错think.run(new ArrayList<>());//编译期会报错

泛型使用

示例

测试类

package com.huke;import java.util.ArrayList;/** * Author:huke * DATE 2023/3/29 10:00 * 泛型 */public class GenericsTest {    public static void main(String[] args) {        GenericsTest genericsTest = new GenericsTest<>();        Dog dog = new Dog();        Cat cat = new Cat();                ArrayList arrayList = new ArrayList<>();        arrayList.add(dog);        arrayList.add(cat);                Think think = new Think<>(arrayList);//指定泛型T,V的界限        think.run(dog);        think.run(cat);        think.run(new Car());//编译期会报错        think.run(new ArrayList<>());//编译期会报错    }}

Think

package com.huke;import java.util.ArrayList;/** * Author:huke * DATE 2023/3/29 10:23 */public class Think {    T t;    V v;    ArrayList arrayList;    Think(ArrayList arrayList) {        this.arrayList = arrayList;    }    void run() {        this.arrayList.forEach(a -> System.out.println(a.toString()));    }    /*   void run(Dog t) {        System.out.println(t);    }    void run(Cat t) {        System.out.println(t);    }    void run(Animal t) {        System.out.println(t);    }*/    void run(T t) {        System.out.println(t);    }   }

泛型在创建对象时就必须确定,如果没有确定则会使用Object

public class WailCardTest {    public static void main(String[] args) {        WailCardTest wailCardTest = new WailCardTest<>();    }}

静态方法的泛型

//静态方法需要声明泛型     public static  T playBall(T t, V v) {        System.out.println("动物们玩球:t:" + t + ",v:" + v);        return t;    }

如果不指定无法通过编译期

// 报错:java: 无法从静态上下文中引用非静态 类型变量 Tpublic static  T playBall(T t, V v) {    System.out.println("动物们玩球:t:" + t + ",v:" + v);    return t;}

静态方法 泛型使用需要方法泛型定义

主要原因

1.Java中的静态方法属于类级别,在类加载时加载进内存,普通方法是在类实例化时才被加载进内存,因此类级别无法访问任何实例变量或方法。对于泛型而言对象不创建泛型无法确认.而静态方法不需要实例化既可以访问

2.当静态方法使用泛型时,Java编译器无法推断出泛型类型,因为在编译时它不知道类被实例化时会传入哪种类型。因此,需要在方法上定义泛型,以便告诉编译器需要使用哪种类型,以便将泛型类型替换为实际类型

通配符

解决了什么问题?

1.使用泛型时难以选择具体类型

2.不希望使用Object类型

3.希望进行编译期检查

通配符使用

通配符不能作为参数入参只能作为引用参数

package com.huke;import java.util.ArrayList;/** * Author:huke * DATE 2023/3/29 15:04 * 通配符 */public class WailCardTest {    public static void main(String[] args) {        WailCardTest wailCardTest = new WailCardTest<>();        ArrayList cats = new ArrayList<>();        cats.add(new Cat());        wailCardTest.play(cats);        ArrayList cars = new ArrayList<>();        cars.add(new Car());        wailCardTest.play(cars);        System.out.println("play方法执行完毕!");        ArrayList animals = new ArrayList<>();        animals.add(new Cat());        wailCardTest.showBySuper(animals);    }    private void play(ArrayList arrayList) {        arrayList.forEach(System.out::println);    }    private void showByExtends(ArrayList arrayList) {        arrayList.forEach(System.out::println);        //arrayList.add(new Cat()); //报错    }    private void showBySuper(ArrayList arrayList) {        arrayList.forEach(System.out::println);        System.out.println("---------");        arrayList.add(new Cat()); //不报错        Object object = arrayList.get(0);        System.out.println("arrayList[0]:"+object);//但是取出来直接成为Object        arrayList.forEach(System.out::println);    }}

输出

CatCarplay方法执行完毕!Cat---------arrayList[0]:CatCatCat

错误写法

void test(? t){//编译不通过}//可以改为void test(ArrayList arrayList) {    arrayList.forEach(System.out::println);}ArrayList arrayList = new ArrayList<>();//这种写法arrayList.add(new Cat());//编译不通过//可以改为ArrayList arrayList = new ArrayList<>();arrayList.add(new Cat());

pecs概念

pecs全称是Producer Extends Consumer Super

使用extends确定上界的只能是生产者,只能往外生产东西,取出的就是上界类型。不能往里塞东西。

使用Super确定下界的只能做消费者,只能往里塞东西。取出的因为无法确定类型只能转成Object类型

  • 用于灵活写入,主要目的是统一使用父类的容器,使得对象可以写入父类型的容器。或者用于比较,使得父类型的比较方法可以应用于子类对象。
  • 用于灵活读取,使得方法可以读取 E 或 E 的任意子类型的容器对象。

示例

//上限private void showByExtends(ArrayList arrayList) {    arrayList.forEach(System.out::println);    //arrayList.add(new Cat()); //报错}//下限private void showBySuper(ArrayList arrayList) {    arrayList.forEach(System.out::println);    System.out.println("---------");    arrayList.add(new Cat()); //不报错    Object object = arrayList.get(0);    System.out.println("arrayList[0]:"+object);//但是取出来直接成为Object    arrayList.forEach(System.out::println);}

泛型擦除

什么是泛型擦除?

泛型是个语法糖,只存在于编译器中。而不存在于虚拟机(JVM)中

编译阶段:编译器对带有泛型的java代码进行编译时,会去执行类型检查和类型推断,然后生成普通的不带泛型的字节码,供JVM接收并执行,此时泛型信息被擦除

示例

测试类

package com.huke;/** * Author:huke * DATE 2023/3/29 17:34 */public class GenericsDeleteTest {    public static void main(String[] args) {        Phone phone = new Phone<>();        phone.setT("绿色");        String color = phone.getT();        System.out.println(color);    }}

Phone

package com.huke;/** * Author:huke * DATE 2023/3/29 17:35 */public class Phone  {    private  T t;    public T getT() {        return t;    }    public void setT(T t) {        this.t = t;    }}

反编译字节码

public class com.huke.GenericsDeleteTest {  public com.huke.GenericsDeleteTest();    Code:       0: aload_0       1: invokespecial #1                  // Method java/lang/Object."":()V       4: return  public static void main(java.lang.String[]);    Code:       0: new           #2                  // class com/huke/Phone       3: dup       4: invokespecial #3                  // Method com/huke/Phone."":()V       7: astore_1       8: aload_1       9: ldc           #4                  // String 绿色      11: invokevirtual #5                  // Method com/huke/Phone.setT:(Ljava/lang/Object;)V      14: aload_1      15: invokevirtual #6                  // Method com/huke/Phone.getT:()Ljava/lang/Object;      18: checkcast     #7                  // class java/lang/String      21: astore_2      22: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;      25: aload_2      26: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V      29: return}

分析

11:开始将String类型转为Object15:get后得到Object类型18:检查类型时候可以转换,转为String

泛型和通配符的区别?

相同点

都可以接收未知参数,都可以指定类型界限

不同点

通配符

当设置泛型通配符上限的时候,只能读取不能写入

当设置泛型通配符下限的时候,可以写入,读取出来就是Object类型

泛型

可以对集合进行添加操作,因为调用泛型方法的时候,类型就已经确定了

关键词: