我是靠谱客的博主 正直方盒,最近开发中收集的这篇文章主要介绍Java学习笔记:初识泛型泛型通配符类型擦除,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 泛型
    • 概念
      • 语法
      • 注意事项
    • 泛型方法
  • 通配符
    • 介绍
    • 泛型上界
      • 语法
      • 读取
      • 写入
    • 泛型下界
      • 语法
      • 读取
      • 写入
  • 类型擦除
      • 注意事项
        • 参考资料

泛型

概念

在编写Java代码时,可能会有将所有类型的元素都可存放入一个数组存储的想法,此时自然而然能想到,使用超类Object类创建Object[]即可接收所有类的对象。但是这样的做法存在弊端:每当要使用Object[]内的元素时,要对元素进行强制转换,这又需要知道取出的元素为哪种类型,否则这种行为是不安全的。这种存储方式便偏离了方便使用的初衷。所以为了方便存储和调用,能否对这个Object[]进行规定?规定其存放的类型,在取用时就可直接使用,若能如此则十分便利。

泛型是JDK5引入的一个特性。泛型机制在Java程序进行编译时进行安全检测,允许使用者在编译阶段检测出非法的类型。

语法

class ClassName<T1,T2,...,Tn>{
}
class ClassName<T1,T2,...,Tn> extends ParentClass<T1>{
}

Java常用中泛型标记符

符号
E - Element元素
T - Type类型(非8种数据类型)
K - Key
V - Value
N - Number数值类型
S,U,V等第二、第三、第四个类型
表示不确定的java类型

注意事项

  • <>菱形运算符中的类型在编译器可根据上下文推导出时可以省略
List<Integer> s = new ArrayList<Integer>();
List<Integer> s = new ArrayList<>();
//两条语句效果相同
  • 泛型将数据类型参数化进行传递,在编译时进行检测。只能使用类,不能使用8种基本的数据类型,若要使用则需要使用其包装类(每一个包装对象是不可变的,存储着基本数据类型原值,并提供获得基本数据类型值的方法,Java5后包装类支持自动装箱/拆箱)

  • 使用表示当前类是泛型类

泛型方法

例:

public class Generics {
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 4, 5};
print(arr);
}
//泛型static方法
public static <E> void print (E[] input){
for (E element: input) {
System.out.printf("%s ", element);
}
System.out.println();
}
}

在泛型方法中的类型参数位于返回类型前

通配符

介绍

通配符< ? >是用来弥补泛型和泛型集合无法协变的不足。

Java中的数组是类型兼容的,叫作协变数组类型。数组是协变的,而泛型和泛型集合不是协变的。

//实现A、B类,B和A间为子类和父类关系
A[] test1 = new B[10];//编译通过
List<A> test2 = new ArrayList<B>();//编译报错
List<?> test3 = new ArrayList<B>();//<?>在此代表可以接收各种类型

假设A类和B类存在继承关系,A类为B类的父类,f(A)为A类更复杂的类型转换,如:ArrayList

协变:变化后保持对应关系,即 B ≤ A -> f(B) ≤ f(A)

逆变:变化后逆转对应关系,即 B ≤ A -> f(A) ≤ f(B)

不变:变化后不满足上述两种关系

但是这样又会带来另外的问题

看下面这段代码

//定义一个泛型类Fruit,提供构造,get和set方法
class Fruit<T>{
private T fruit;
public Fruit() {
}
public Fruit(T fruit) {
this.fruit = fruit;
}
public void setFruit(T fruit) {
this.fruit = fruit;
}
public T getFruit() {
return fruit;
}
}
public class Generics {
//main方法
public static void main(String[] args) {
Fruit<String> test1 = new Fruit<>("草莓");
Fruit<Integer> test2 = new Fruit<>(3);
printFruit(test1);//输出"草莓"
printFruit(test2);//无法编译,因为printFruit方法中要求的参数类型为Fruit<String>
}
public static void printFruit(Fruit<String> fruit){
System.out.println(fruit.getFruit());
}
}

此时我们只需将上述printFruit方法改成如下,即可运行

public static void printLife(Fruit<?> fruit){
System.out.println(fruit.getShelfLife());
}

但是上述的改法并不安全,因为<?>可以接收任何泛型,这不是我们想要看到的,应当要加以限制,这就引出了泛型上界泛型下界

泛型上界

泛型上界基于协变性质

语法

<? extends 上界>
public static int count(Fruit<? extends Fruit> fruit){
//可传入的实参类型被限定为Fruit及其子类
}

? extends设置的上界不能写入数据,只能进行数据读取

//在public class Generics中添加该方法,同时构建一个Apple类继承Fruit类
public static void correctFruit(Fruit<? extends Fruit> fruit){
fruit.setFruit(new Apple());//发生错误
}

对于? extends限定的类型操作

List<? extends Fruit> wareHouse;
//Fruit为父类,Apple,Banana,Melon等为子类

读取

对于wareHouse来说可以读取到Fruit的对象,因为其中存储的是为Fruit类和Fruit的子类,而不一定能读取到Apple,因为可能其中存储的是Banana或Melon,出于数据安全考虑,一般用Fruit(基类)进行接收

写入

对于wareHouse来说,我们不能存储Fruit入wareHouse中,因为wareHouse可能为List,不能存入Apple,因为wareHouse有可能为List,所以不能进行写入操作

泛型下界

泛型下界基于逆变性质

语法

<? super 下界>
public static int count(Fruit<? super Fruit> fruit){
//可传入的实参类型被限定为Fruit及其父类
}

? super设置的上界不能读取数据,只能进行数据存储

对于? super限定的类型操作

List<? super Fruit> wareHouse;

读取

对于wareHouse来说,我们不能保证能读取到Fruit类型的数据,因为wareHouse可能存储的是Fruit的父类类型数据;不一定能读取Fruit的父类类型数据,有可能其中存储的是Object类型的数据,可以确定的是我们能读取的是Object类型的实例(见泛型擦除)

写入

对于wareHouse来说,可以存入Fruit及其子类类型的数据,但是不能存入Fruit父类,如Object类类型数据

类型擦除

泛型类可以由编译器通过类型擦除转变成非泛型类

class Fruit<T>{
//在编译器编译时,T 会被替换成Object
}

编译器生成了一种与泛型类同名的原始类,类型参数都被删去,类型变量由类型界限来代替,这就是擦除机制

优点:程序员可省略一些类型转换代码,由编译器进行类型检查

注意事项

  • 不能创建一个泛型类型的实例

  • T obj = new T();	//非法
    
  • 不能创建一个泛型数组

  • T[] obj = new T[5];	//非法
    
  • 不能进行参数化类型的数组的实例化

  • Fruit<String>[] arr = new Fruit<String>[10];//非法
    

参考资料

[1] Java 之泛型通配符 ? extends T 与 ? super T 解惑

[2] 几个搞不太懂的术语:逆变、协变、不变

[3] 数据结构与算法分析 [美]Mark Allen Weiss

本文的主要目的是充当学习笔记,同时强化学习效果,且兼有分享所学知识之意,欢迎批评指正

最后

以上就是正直方盒为你收集整理的Java学习笔记:初识泛型泛型通配符类型擦除的全部内容,希望文章能够帮你解决Java学习笔记:初识泛型泛型通配符类型擦除所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(28)

评论列表共有 0 条评论

立即
投稿
返回
顶部