page contents

Java教程——Java泛型详解

本文讲述了Java教程——Java泛型详解!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2023-09-Ot9vxKPD6503b369e1859.jpg

本文讲述了Java教程——Java泛型详解!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

从Java1.5开始,官方引入了泛型机制,事实上,从1.3版本开始,泛型就已经存在了,经过历代的发展,已足具雏形,本篇文章就来学习一下泛型的使用。

01、认识泛型

在此之前,我们先来认识一下泛型吧,先看一段示例:

public class Main {

    public static void main(String[] args) {

        List list = new ArrayList();

        list.add(1);

        list.add(2);

        list.add(3);

        dealList(list);

    }


    public static void dealList(List list) {

        int result = 0;

        for (Object obj : list) {

            int num = (int) obj;

            result += num;

        }

        System.out.println(result);

    }

}

在该示例中,dealList方法用于计算一个集合中的元素和,当然,只有数字才能够参与运算,但是示例中的list是可以存放任意对象的,所以很可能会出现如下情况:

public static void main(String[] args) {

    List list = new ArrayList();

    list.add(1);

    list.add(2);

    list.add(3);

    list.add("4");

    dealList(list);

}

因为字符串无法转为整型,所以程序会报错:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

02、定义泛型类

泛型的用法十分多样,它可以作用在类、方法上实现各种功能,先来了解一下泛型类的定义。

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Genericity {


    private String str1;

    private String str2;

}

以上是一个简单的Java类,我们想通过它计算其中两个属性值的和:


public static void main(String[] args) {

    Genericity genericity = new Genericity("1","1");   

    String str1 = genericity.getStr1();

    String str2 = genericity.getStr2();

    Integer num1 = Integer.valueOf(str1);

    Integer num2 = Integer.valueOf(str2);

    int result = num1 + num2;

    System.out.println(result);

}

但此时我觉得将属性定义为String是一个错误的决定,因为计算属性值的和还需要自己对类型进行转换,所以我将属性调整为了Integer类型:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Genericity {


    private Integer str1;

    private Integer str2;

}

当然了,这个例子可能有一些牵强,事实上不会有人这么做,但目的很明显,就是想让类中的属性类型可变,使得整个类更加灵活,基于此需求,我们可以使用泛型对其进行改造:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Genericity<T> {

    private T t1;

    private T t2;

}

这就是泛型类的定义,通过在类名后面添加<T>符号即可定义泛型类,而类中的属性类型均为T,这将导致类中的属性类型会跟随T的变化而变化,用法如下:

public static void main(String[] args) {

    Genericity<Integer> genericity = new Genericity<>(1, 1);

    Integer t1 = genericity.getT1();

    Integer t2 = genericity.getT2();

    int result = t1 + t2;

    System.out.println(result);

}

在创建泛型类对象时,同样在类名后添加<Integer>,此时Integer就作为了T符合的内容,所以类中的属性都将是Integer类型。如果类名后添加<String>,则类中的属性均为String类型。

实际上,泛型类可以定义多个泛型变量,比如:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Genericity<T,U> {


    private T t1;

    private U t2;

}

此时我们在创建对象时就可以传入两个类型:


public static void main(String[] args) {

    Genericity<Integer,String> genericity = new Genericity<>(1, "1");

    Integer t1 = genericity.getT1();

    String t2 = genericity.getT2();

}

03、定义泛型方法

泛型除了作用在类上,还可以作用在方法上,效果与泛型类是相似的。

public static <T> void method(T t) {

}

只需在调用方法时传入类型即可:

public static void main(String[] args) {

    Genericity.<Integer>method(1);

}

而事实上,你无需这么写,因为编译器能够自动推断出泛型类型T,所以调用泛型方法可以简写为:

public static void main(String[] args) {

    Genericity.method(1);

}

泛型方法的返回值也可以使用类型变量:

public static <T> T method(T t) {

    return t;

}

当然了,泛型方法也支持定义多个类型变量:


public static <T,U> T method(T t,U u) {

    return t;

}

04、泛型的限定符

来看下面的程序:

public static <T> T min(T[] t) {

    T minimum = t[0];

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

        if (minimum.compareTo(t[i]) > 0) {

            minimum = t[i];

        }

    }

    return minimum;

}

能看得出来这段程序在干什么吗?是的,它能够取出数组t中的最小值,然而这段程序是有问题的,因为T可以是任意类型的对象,但不是什么对象都能够调用compareTo方法进行比较的,所以,我们需要对类型变量T进行限定,限定为实现了Comparable接口的对象,如下:

public static <T extends Comparable> T min(T[] t) {

    T minimum = t[0];

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

        if (minimum.compareTo(t[i]) > 0) {

            minimum = t[i];

        }

    }

    return minimum;

}

一个泛型方法也可以对类型变量进行多个限定:


public static <T extends Comparable & Serializable> T min(T[] t) {

    ......

}

05、泛型擦除

需要知道的是,泛型仅仅是在编译期间起作用,目的是让程序员在编译期间就避免发生一些类型不对应的问题,而在运行阶段,泛型是根本不存在的,因为虚拟机会对泛型进行擦除。

我们可以通过反射进行验证,因为反射是作用在运行阶段的:

public static void main(String[] args) throws Exception {

    List<Integer> list = new ArrayList<>();

    list.add(1);

    Method addMethod = list.getClass().getDeclaredMethod("add",Object.class);

    addMethod.invoke(list,"2");

    addMethod.invoke(list,true);

    addMethod.invoke(list,3.2f);

    System.out.println(list);

}

若是通过反射能够将这些非Integer类型的值存入list,则说明在运行期间确实是不存在泛型检查的,运行结果如下:

[1, 2, true, 3.2]

泛型擦除也体现在泛型方法中,回顾之前的例子:

public static <T extends Comparable> T min(T[] t) {

    ......

}

当程序运行期间,泛型会被擦除,此时方法变为如下:

public static Comparable min(Comparable t) {

    ......

}

这也就是为什么类型限定能够生效的原因了,通过泛型擦除后,该方法就只能接收和返回Comparable接口的实现类。

06、泛型通配符

固定的泛型类型显然无法满足复杂多变的需求,为此,泛型设计者们还提供了泛型通配符,如:

Genericity<? extends Person>

它表示类型变量必须是Person类型的子类。当然了,泛型通配符还有超类型定义的情况:

Genericity<? super Person>

此时类型变量就必须是Person类的超类。

还有一种情况是无限定通配符:

Genericity<?>

它和Genericity<T>非常相似,但又有不同,Genericity<?>的setter方法不能被调用,getter方法只能返回Object类型,不过这种方式的用法较少,可能会被用来判断空引用:

public static boolean isNull(Genericity<?> genericity){

    return genericity.getContent();

}

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

想高效系统的学习Java编程语言,推荐大家关注一个微信公众号:Java圈子。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Java入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2023-03-2AoKIjPQ64014b4ad30a3.jpg

  • 发表于 2023-09-15 09:29
  • 阅读 ( 211 )
  • 分类:行业资讯

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1316 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章