【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制)

这篇具有很好参考价值的文章主要介绍了【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

泛型

1. 什么是泛型

2.泛型方法

3.通配符上界(泛型的协变)

4.通配符下界(泛型的逆变)

5.泛型的编译(擦除机制)


泛型

        泛型:就是让一个类能适用于多个类型,就是在封装数据结构时能让封装的类型被各种类型使用所以引入了泛型的概念,虽然有了泛型,什么数据都可以放,但是更多情况下我们还是希望他只能持有一种数据类型。所以,泛型的主要目的:指定当前的容器,要持有什么类型的对象,让编译器去做检查。

1. 什么是泛型

语法格式如下:

泛型类<类型实参>变量名;//定义一个泛型类引用

new 泛型类<类型实参>(构造方法实参);//实例化一个泛型类对象

一般用<T>作为占位符 ,表示当前类是一个泛型类。Java中的泛型参数只能是引用类型,不能是基本类型,这与Java的泛型擦出机制有关。

 实例:

MyArray<Integer> list = new MyArray<Integer>();

*裸类型(Raw Type)      (这是一个泛型类但没有带着类型实参) 

MyArray list = new MyArray();

裸类型是为了兼容老版本的API保留机制,我们不要轻易使用。 

2.泛型方法

 泛型方法:定义一个泛型方法,我们需要在方法返回值前使用尖括号声明一个或多个泛型参数然在方法中就可以用到声明的泛型参数了,调用泛型方法时,我们不需要手动写出类型,编译器会根据你的调用,自动推导出具体类型。

静态泛型方法:泛型类有一个局限,静态方法和静态属性访问不了类上定义的泛型参数,静态泛型方法的定义和使用与普通泛型方法一致。

泛型类和泛型方法的使用场景:

当泛型参数需要在多个方法或成员属性间扭转,就使用泛型类,比如:集合。

当泛型参数只需要作用于某个方法,那就使用泛型方法。

【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制),【JavaSE】,java,开发语言

3.通配符上界(泛型的协变)

泛型类型是具有不变性的,比如下面代码就是错误的:

Arraylist<Object> objectList;
ArrayList<String> stringList = new ArrayList<>();
objectList=stringList//这里会报错

objectList.add(new Shit());
String str = stringList.get(0);
//因为我们无法将一个object对象转化为string对象,所以在编译层面上面的赋值就会直接报错

 为了让泛型变得更灵活,Java引入了通配符:?,通过下面的代码来给大家介绍一下通配符的作用:

在不使用通配符时,因为泛型的不变性,下面这段代码会出现问题,就使代码非常不灵活。

public static double sum(List<Number> list){
   double result =0;
   for(Number number : list){
        result += number.doubleValue();
   }
    return result;
}

List<Double> doubleList = new ArrayList<>();
sum(doubleList)//这里会报错

我们可以使用通配符上界(?:extends T)来使代码更灵活

public static double sum(List<? extends Number> list){
   double result =0;
   for(Number number : list){
        result += number.doubleValue();
   }
    return result;
}

List<Double> doubleList = new ArrayList<>();
sum(doubleList)

这种写法也被叫做泛型的协变 。

4.通配符下界(泛型的逆变)

我们还可以使用通配符下界(?:super T)来使代码变得灵活,代码实例如下:

class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Plate<T> {
private T plate ;
public T getPlate() {
return plate;
}
public void setPlate(T plate) {
this.plate = plate;
}
}
public class TestDemo {
public static void main(String[] args) {
Plate<Fruit> plate1 = new Plate<>();
plate1.setPlate(new Fruit());
fun(plate1);
Plate<Food> plate2 = new Plate<>();
plate2.setPlate(new Food());
fun(plate2);
}
public static void fun(Plate<? super Fruit> temp){
// 此时可以修改!!添加的是Fruit 或者Fruit的子类
temp.setPlate(new Apple());//这个是Fruit的子类
temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类
System.out.println(temp.getPlate());//只能直接输出
}
}

通配符的优缺点:

协变:放宽了对子类类型的泛型约束,但是缺点是不能对调用的参数进行写入数据只能进行读取数据。

逆变:放宽了对父类类型的泛型约束,但是缺点是不能对参数进行读取数据,只能写入数据。

5.泛型的编译(擦除机制)

擦除机制的实质就是,在编译阶段,Java的泛型类型可能是ArrayList<Integer>但是在java文件编译成字节码的过程中,泛型参数部分就被擦出了(泛型类,泛型方法的参数全部被替换成它的第一个上界或者顶级父类Object),在class文件中,无论参数是什么,JVM实际执行的代码类型其实是ArrayList<Object>类型,这也就引出了很多问题如下:

  1. 泛型参数只能是引用类型而不能是基本数据类型,因为基本数据类型无法被擦除成Object。
  2. 不能使用instanceof关键字进行泛型类型检测,因为在运行时所以的泛型类型都是裸类型。
  3. 泛型类型无法实例化类型参数T a=new T(),因为在运行时无法确定T的具体类型,也不知道T是否存在无参构造器。
  4. 无法实例化泛型数组T[] arry =new T[2];因为泛型最后都被擦除成Object数组,在使用时很容易发生类型转化异常,比如object转化不成string。

擦除机制是Java为了引入泛型这个语法而不得不做出的妥协之举,泛型语法是JDK5之后引入的,为了兼容老版本,不得不在编译阶段将泛型擦除成裸类型。但是在其他语言中,泛型的使用会非常自然且简单安全,在编写代码是我们要了解泛型擦除机制,否则可能会引发很多不必要的异常。

类型擦除是指在运行时对于JVM而言泛型参数被擦除掉了,并不代表泛型信息消失了,才class文件中泛型信息被以其他方式进行保存,我们依然可以在运行时通过反射的手段进行泛型类型检测。文章来源地址https://www.toymoban.com/news/detail-763613.html

到了这里,关于【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 基础进阶篇(十)—— 泛型与可变参数

    泛型是 JDK5 中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。 格式: 数据类型; 好处:统一数据类型。把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。 注意: 泛型只能支持引用数据类型 。集合

    2024年02月03日
    浏览(44)
  • Java中泛型和Object类型 初级进阶教程(一)

    在学习的过程中,常常看到某个类或者接口等中使用 ListT, TestT,其中T的作用是什么呢? 1 在类中使用泛型 2 使用多个泛型 3 在类中使用泛型 4 在方法中使用泛型 5 限制泛型类型 6 通配符 (Wildcard) 总结:泛型和Object类型之间的区别 类型安全: 泛型 T : 泛型提供了编译时类型

    2024年02月01日
    浏览(54)
  • 泛型类、泛型接口、泛型方法详解!

    Java泛型中的那些字母是什么意思?(E、T、K、V、S) 当定义类、接口或方法时,可以使用类型参数(Type Parameters)来表示未知的类型,从而使代码更加通用和灵活。下面分别给出类、接口和方法的例子: 在这个例子中, Box 类使用类型参数 T 来表示未知的类型,可以在创建对象

    2023年04月25日
    浏览(47)
  • 【JavaSE】Java入门九(异常详解)

    目录 异常  1.Java中异常的体系结构 2.异常的处理 3.自定义异常类        在Java中,将程序执行过程中发生的不正常行为称为异常,C语言中没有这个概念,接下来我们重点需要掌握异常处理体系(try, catch, throw, finally)以及如何自定义异常类。 异常的种类繁多,Java内部维护了

    2024年02月03日
    浏览(41)
  • 详解Java中的泛型(泛型的语法,擦除机制,泛型的上界)

    目录 一.什么是泛型 二.Java中为什么要使用泛型 三.泛型的语法 四.泛型类的使用 五.泛型的编译机制(擦除机制) 六.泛型的上界 泛型(Generics)是Java SE 5中引入的一个新特性,可以 使Java中的类和方法具有更广泛的类型范围 。通俗的说,它使得我们可以在定义类和方法时指定

    2024年02月05日
    浏览(48)
  • java的泛型【详解】

    定义类、接口、方法时,同时声明了一个或者多个类型变量(如:E) ,称为泛型类、泛型接口,泛型方法、它们统称为泛型。 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。 1.泛型类:

    2024年02月19日
    浏览(42)
  • Java的泛型详解

    Java泛型是一种编程语言的特性,它允许类、接口和方法在定义时使用一个或多个类型参数,这些类型参数在调用时会被实际类型替换,从而增强了代码的重用性和类型安全性。通过使用泛型,我们可以编写出更加通用的代码,同时也可以减少代码中的强制类型转换操作,提高

    2024年02月04日
    浏览(40)
  • Java-泛型机制详解

    Java集合(Collection)中元素的类型是多种多样的。例如,有些集合中的元素是Byte类型的,而有些则可能是String类型的,等等。Java允许程序员构建一个元素类型为Object的Collection,其中的元素可以是任何类型在[Java SE](https://baike.baidu.com/item/Java SE/4662159?fromModule=lemma_inlink) 1.5之前,

    2023年04月10日
    浏览(43)
  • 【JavaSE】Java方法的使用

    【本节目标】 1. 掌握方法的定义以及使用 2. 掌握方法传参 3. 掌握方法重载 4. 掌握递归 目录 1.方法概念及使用 1.1什么是方法(method) 1.2 方法定义 1.3 方法调用的执行过程 1.4 实参和形参的关系 2. 方法重载 2.1 为什么需要方法重载 2.2 方法重载概念 3. 递归 3.1 生活中的故事 3.2 递

    2024年02月12日
    浏览(40)
  • Java中泛型详解,非常详细

    在前面的几篇文章中,详细地给大家介绍了Java里的集合。但在介绍集合时,我们涉及到了泛型的概念却并没有详细学习, 所以今天我们要花点时间给大家专门讲解什么是泛型、泛型的作用、用法、特点等内容。 有些粉丝朋友,在之前就一直很好奇,比如List String 中的 Strin

    2024年02月07日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包