工作几年后再来总结下Java泛型

简介: 泛型是 Java 的高级特性之一,如果想写出优雅而高扩展性的代码,或是想读得懂一些优秀的源码,泛型是绕不开的槛。

引子


想写一下关于 Java 一些高级特性的文章,虽然这些特性在平常实现普通业务时可能用不到,但如果想写出优雅而高扩展性的代码,或是想读得懂一些优秀的源码,这些特性又是不可避免的。


如果对这些特性不了解,不熟悉特性的应用场景,使用时又因为语法等原因困难重重,很难让人克服惰性去使用它们,所以身边总有一些同事,工作了很多年,却从没有用过 Java 的某些高级特性,写出的代码总是差那么一点儿感觉。


为了避免几年后自己的代码还是非常 low,准备从现在开始深入理解一下这些特性。本文先写一下应用场景最多的泛型。


是什么


首先来说泛型是什么。泛型的英文是 generic,中文意思是通用的、一类的,结合其应用场景,我理解泛型是一种 通用类型。但我们一般指泛型都是指其实现方式,也就是 将类型参数化


对于 Java 这种强类型语言来说,如果没有泛型的话,处理相同逻辑不同类型的需求会非常麻烦。


如果想写一个对 int 型数据的快速排序:

public static void quickSort(int[] data, int start, int end) {
    int key = data[start];
    int i = start;
    int j = end;
    while (i < j) {
        while (data[j] > key && j > i) {
            j--;
        }
        data[i] = data[j];
        while (data[i] < key && i < j) {
            i++;
        }
        data[j] = data[i];
    }
    data[i] = key;
    if (i - 1 > start) {
        quickSort(data, start, i - 1);
    }
    if (i + 1 < end) {
        quickSort(data, i + 1, end);
    }
}

可是如果需求变了,现在需要实现 int 和 long 两种数据类型的快排,那么我们需要利用 Java 类方法重载功能,复制以上代码,将参数类型改为 double 粘贴一遍。可是,如果还要实现 float、double 甚至字符串、各种类的快速排序呢,难道每添加一种类型就要复制粘贴一遍代码吗,这样未必太不优雅。


当然我们也可以声明传入参数为 Object,并在比较两个元素大小时,判断元素类型,并使用对应的方法比较。这样,代码就会恶心在类型判断上了。不优雅的范围小了一点,并不能解决问题。


这时,我们考虑使用通用类型(泛型),将快排方法的参数设置为一个通用类型,无论什么样的参数,只要实现了 Comparable 接口,都可以传入并排序。


public static  <T extends Comparable<T>> void quickSort(T[] data, int start, int end) {
    T key = data[start];
    int i = start;
    int j = end;
    while (i < j) {
        while (data[j].compareTo(key) > 0 && j > i) {
            j--;
        }
        data[i] = data[j];
        while (data[i].compareTo(key) < 0 && i < j) {
            i++;
        }
        data[j] = data[i];
    }
    data[i] = key;
    if (i - 1 > start) {
        quickSort(data, start, i - 1);
    }
    if (i + 1 < end) {
        quickSort(data, i + 1, end);
    }
}

那么,可以总结一下泛型的应用场景了,当遇到以下场景时,我们可以考虑使用泛型:


  • 当参数类型不明确,可能会扩展为多种时。
  • 想声明参数类型为 Object,并在使用时用 instanceof 判断时。


需要注意,泛型只能替代Object的子类型,如果需要替代基本类型,可以使用包装类,至于为什么,会在下文中说明。


怎么用


泛型的声明使用 <占位符 [,另一个占位符] > 的形式,需要在一个地方同时声明多个占位符时,使用 , 隔开。占位符的格式并无限制,不过一般约定使用单个大写字母,如 T 代表类型(type),E 代表元素*(element)等。虽然没有严格规定,不过为了代码的易读性,最好使用前检查一下约定用法。


泛型指代一种参数类型,可以声明在类、方法和接口上。


泛型类


class Generics<T> { // 在类名后声明引入泛型类型
    private T field;  // 引入后可以将字段声明为泛型类型
    public T getField() { // 类方法内也可以使用泛型类型
        return field;
    }
}


泛型方法


public [static] <T> void testMethod(T arg) { // 访问限定符[静态方法在 static] 后使用 <占位符> 声明泛型方法后,在参数列表后就可以使用泛型类型了
    // doSomething
}


调用


然后是泛型的调用,泛型的调用和普通方法或类的调用没有什么大的区别,如下:

public static void main(String[] args) {
  String[] strArr = new String[2];
     // 泛型方法的调用跟普通方法相同
     Generics.quickSort(strArr, 0, 30 );
     // 泛型类在调用时需要声明一种精确类型
     Generics<Long> sample = new Generics<>();
     Long field = sample.getField();
    }
    // 泛型接口需要在泛型类里实现
    class GenericsImpl<T> implements Comparable<T> {
    @Override
    public int compareTo(T o) {
        return 0;
    }
}


小结


好好理了一下泛型,感觉收获颇多,Java 迷雾被拨开了一些。这些特性确实挺难缠,每当自己觉得已经理解得差不多的时候,过段时间又觉得当初理解得还不够,重要的还是要实践,在使用时会很容易发现疑惑的地方。

目录
相关文章
|
3月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
75 2
|
12天前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
12 0
[Java]泛型
|
21天前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
10 1
|
29天前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
26 5
|
26天前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
17 1
|
2月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
34 9
Java——包装类和泛型
|
29天前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
18 2
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
26天前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
13 0
|
2月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。