java泛型和通配符
Posted dogtwo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java泛型和通配符相关的知识,希望对你有一定的参考价值。
java泛型/通配符
泛型
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(表示Java 类,包括基本的类和我们自定义的类)
- K - Key(表示键,比如Map中的key)
- V - Value(表示值)
- N - Number(表示数值类型)
- ? - (表示不确定的java类型)
为什么会有泛型?
1.需要解决代码冗余,提高复用性
2.需要编译期的检查
编译期检查
ArrayList<Animal> arrayList = new ArrayList<>();
arrayList.add(dog);
arrayList.add(new Object());//编译器这里会报错
运行结果
java: 对于add(java.lang.Object), 找不到合适的方法
提高代码复用性
Dog
和 Cat
分别继承 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<T extends Animal,V extends Car>
public static void main(String[] args)
GenericsTest<Animal> genericsTest = new GenericsTest<>();
Dog dog = new Dog();
Cat cat = new Cat();
ArrayList<Animal> arrayList = new ArrayList<>();
arrayList.add(dog);
arrayList.add(cat);
Think<Animal, Car> 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, V>
T t;
V v;
ArrayList<T> arrayList;
Think(ArrayList<T> 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<T>
public static void main(String[] args)
WailCardTest<Object> wailCardTest = new WailCardTest<>();
静态方法的泛型
//静态方法需要声明泛型 <T, V>
public static <T, V> T playBall(T t, V v)
System.out.println("动物们玩球:t:" + t + ",v:" + v);
return t;
如果不指定无法通过编译期
// 报错:java: 无法从静态上下文中引用非静态 类型变量 T
public 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<T>
public static void main(String[] args)
WailCardTest<Object> wailCardTest = new WailCardTest<>();
ArrayList<Cat> cats = new ArrayList<>();
cats.add(new Cat());
wailCardTest.play(cats);
ArrayList<Car> cars = new ArrayList<>();
cars.add(new Car());
wailCardTest.play(cars);
System.out.println("play方法执行完毕!");
ArrayList<Animal> animals = new ArrayList<>();
animals.add(new Cat());
wailCardTest.showBySuper(animals);
private void play(ArrayList<?> arrayList)
arrayList.forEach(System.out::println);
private void showByExtends(ArrayList<? extends Animal> arrayList)
arrayList.forEach(System.out::println);
//arrayList.add(new Cat()); //报错
private void showBySuper(ArrayList<? super Animal> 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);
输出
Cat
Car
play方法执行完毕!
Cat
---------
arrayList[0]:Cat
Cat
Cat
错误写法
void test(? t)//编译不通过
//可以改为
void test(ArrayList<?> arrayList)
arrayList.forEach(System.out::println);
ArrayList<?> arrayList = new ArrayList<>();//这种写法
arrayList.add(new Cat());//编译不通过
//可以改为
ArrayList<? super Animal> arrayList = new ArrayList<>();
arrayList.add(new Cat());
pecs概念
pecs
全称是Producer Extends Consumer Super
使用extends确定上界的只能是生产者,只能往外生产东西,取出的就是上界类型。不能往里塞东西。
使用Super确定下界的只能做消费者,只能往里塞东西。取出的因为无法确定类型只能转成Object类型
- 用于灵活写入,主要目的是统一使用父类的容器,使得对象可以写入父类型的容器。或者用于比较,使得父类型的比较方法可以应用于子类对象。
- 用于灵活读取,使得方法可以读取 E 或 E 的任意子类型的容器对象。
示例
//上限
private void showByExtends(ArrayList<? extends Animal> arrayList)
arrayList.forEach(System.out::println);
//arrayList.add(new Cat()); //报错
//下限
private void showBySuper(ArrayList<? super Animal> 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<String > 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 <T>
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."<init>":()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."<init>":()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类型转为Object
15:get后得到Object类型
18:检查类型时候可以转换,转为String
泛型和通配符的区别?
相同点
都可以接收未知参数,都可以指定类型界限
不同点
通配符
当设置泛型通配符上限的时候,只能读取不能写入
当设置泛型通配符下限的时候,可以写入,读取出来就是Object类型
泛型
可以对集合进行添加操作,因为调用泛型方法的时候,类型就已经确定了
以上是关于java泛型和通配符的主要内容,如果未能解决你的问题,请参考以下文章