深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

在这里插入图片描述

​ 在Java的中,有一个强大的工具,它可以让你在编写代码时既保持类型安全,又享受灵活性。**这个工具就是——泛型(Generics)。**本文将引导你深入了解Java泛型的奥秘,让你从此编写更强大、更安全的代码。

基本概念:

泛型是什么?

​ 泛型是Java中的一项强大功能,它允许你编写能够处理多种数据类型的类、接口和方法。通过使用泛型,你可以将数据类型作为参数传递给类或方法,从而实现更高层次的代码重用和类型安全。

Java 泛型(generics)本质是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

答案是可以使用 Java 泛型

使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


泛型类和泛型方法

泛型在Java中有两种主要形式:泛型类和泛型方法。泛型类是具有一个或多个类型参数的类,而泛型方法是在调用时可以接受不同类型参数的方法。


泛型类的基本概念

泛型类是一种允许在创建类的实例时指定数据类型的类。通过使用泛型,你可以将类的数据类型参数化,从而使得类可以适用于多种数据类型。让我们来看一个简单的例子:一个通用的容器类Box

public class Box<T> {
    private T content;

    public Box(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    public void setContent(T content) {
        this.content = content;
    }
}

在上面的示例中,我们定义了一个泛型类Box,它可以存储任意类型的数据。T是一个类型参数,它在实例化Box对象时确定。

使用泛型类

现在,让我们看看如何使用泛型类Box来存储不同类型的数据。

public class GenericClassExample {

    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>(42);//存储整数
        int intValue = intBox.getContent();
        System.out.println("Integer value: " + intValue);

        Box<String> stringBox = new Box<>("Hello, Stevedash!");//存储字符串
        String stringValue = stringBox.getContent();
        System.out.println("String value: " + stringValue);
    }
}

在上述示例中,我们创建了两个Box对象,一个存储整数,另一个存储字符串。通过使用泛型类,我们可以在编译时就确保数据类型的正确性,避免了在运行时出现类型错误。


类型安全与重用性

泛型类不仅提供了类型安全性,还增加了代码的重用性。你可以轻松地创建适用于不同类型的通用类,减少了代码的冗余。这使得你的程序更加健壮、可维护。


泛型类的限制

虽然泛型类非常强大,但也有一些限制。例如,无法创建泛型数组,也不能使用基本数据类型作为泛型参数(需要使用对应的包装类)。

自定义泛型类

除了使用Java提供的泛型类,你还可以自定义泛型类来满足特定需求。通过实现自己的泛型类,你可以更好地理解泛型的原理,以及如何灵活地使用它。


泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。

  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。

  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。

  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。


    java 中泛型标记符:

  • E - Element (在集合中使用,因为集合中存放的是元素)

  • T - Type(Java 类)

  • K - Key(键)

  • V - Value(值)

  • N - Number(数值类型)

  • ? - 表示不确定的 java 类型


下面的例子演示了如何使用泛型方法打印不同类型的数组元素:

实例

public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }
 
    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
 
        System.out.println( "整型数组元素为:" );
        printArray( intArray  ); // 传递一个整型数组
 
        System.out.println( "\n双精度型数组元素为:" );
        printArray( doubleArray ); // 传递一个双精度型数组
 
        System.out.println( "\n字符型数组元素为:" );
        printArray( charArray ); // 传递一个字符型数组
    } 
}

编译以上代码,运行结果如下所示:

整型数组元素为:
1 2 3 4 5 

双精度型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O 

在这段代码中,虽然我们使用了泛型方法printArray来输出不同类型的数组元素,但实际上并没有创建一个泛型数组。我们只是在使用已有的数组来进行迭代遍历并输出元素,而不是通过泛型参数创建新的数组。


在Java中,直接创建一个泛型数组是不允许的。例如,以下代码是不合法的:

// 这是不合法的,无法直接创建泛型数组
T[] array = new T[10];

​ 泛型数组的限制是为了确保类型安全性。虽然在代码中使用了泛型方法,但我们并没有在代码中直接创建泛型数组,而是使用了已有的数组。因此,虽然代码中有泛型方法,但没有涉及创建泛型数组的情况。


通配符和边界

泛型还支持通配符和边界的概念,让你能够更精细地控制泛型类型的范围。通配符?表示未知类型,而边界则限制了允许传递的类型范围。

import java.util.List;

public class GenericWildcard {

    /**
     * 打印通配符类型的列表元素
     *
     * @param list 要打印的列表
     */
    public static void printList(List<?> list) {
        for (Object element : list) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    /**
     * 计算数字类型列表的总和
     *
     * @param list 要计算总和的列表
     * @return 列表元素的总和
     */
    public static <T extends Number> double sumOfList(List<T> list) {
        double sum = 0;
        for (T element : list) {
            sum += element.doubleValue();
        }
        return sum;
    }//<T extends Number>: 这是泛型类型参数的声明。在这里,<T> 表示泛型类型参数的名称,你可以在方法体内使用它来代表实际的数据类型。extends Number 是一个泛型类型的限定符。它的意思是,类型参数 T 必须是 Number 类或其子类。这样做的目的是为了确保列表中的元素都是数字类型,以便在方法中进行数字运算。

    public static void main(String[] args) {
        // 创建一个包含不同类型元素的列表
        List<?> wildcardList = List.of("Hello", 42, 3.14, 'A');

        // 打印通配符类型的列表元素
        System.out.print("通配符列表元素:: ");
        printList(wildcardList);

        // 创建一个数字类型的列表
        List<Integer> integerList = List.of(1, 2, 3, 4, 5);

        // 计算数字类型列表的总和
        double sum = sumOfList(integerList);

        // 输出计算结果
        System.out.println("整数列表总和: " + sum);
    }
}

​ 输出结果如下:

通配符列表元素: Hello 42 3.14 A 
整数列表总和: 15.0

​ 上面的示例演示了一个使用通配符的方法printList和一个使用边界的方法sumOfList。通配符使方法能够接受不同类型的列表,而边界则限制了方法只能接受继承自Number的类型列表。

泛型与集合框架

Java的集合框架(如List、Set、Map等)广泛使用了泛型,使得集合中的元素类型可以更加明确和安全。使用泛型可以在编译时捕获类型错误,避免在运行时出现类型转换异常。


最后总结:

​ Java泛型是一项强大的功能,它既保持了类型安全,又提高了代码的灵活性和重用性。通过学习泛型的使用,能够编写更强大、更安全的代码,让我们的程序更加健壮和可维护。

总结一下泛型类的优缺点:

泛型类的优点:

  1. 类型安全性: 泛型类在编译时可以检查数据类型,从而提前发现类型错误,避免了在运行时出现类型转换异常。
  2. 代码重用性: 泛型类允许你创建通用的数据结构,适用于多种数据类型,减少了代码的冗余,提高了代码的重用性。
  3. 灵活性: 泛型类使得你可以创建适用于不同数据类型的通用类,从而在不同场景下实现灵活的数据存储和处理。
  4. 编译时类型检查: 泛型类使得编译器能够检查和强制确保类型的正确性,从而提高了代码的可靠性和稳定性。
  5. 更好的可读性: 使用泛型类可以提供更清晰的代码结构和命名,使得代码更易于理解和维护。

泛型类的缺点:

  1. 类型参数限制: 泛型类无法直接使用基本数据类型作为类型参数,需要使用对应的包装类。同时,无法创建泛型数组。
  2. 复杂性: 对于初学者来说,泛型的概念可能会有一定的复杂性,需要一些时间来理解和掌握。
  3. 编译时错误信息: 有时,编译器产生的泛型相关的错误信息可能不够直观,需要花时间来解读和修复。
  4. 擦除和类型擦除: 泛型类在编译时会进行类型擦除,这可能会导致在某些情况下失去一些类型信息。
  5. 部分场景不适用: 在某些特定的场景下,泛型类可能不适用,需要根据实际需求来选择是否使用泛型。

​ 尽管泛型类存在一些缺点,但它的优点远远超过了缺点,可以帮助我们写出更安全、更灵活、更高效的代码。

作者:Stevedash

发表于:2023年8月8日15点19分

来源:Java 泛型 | 菜鸟教程 (runoob.com)

注:本文内容基于个人学习理解,如有错误或疏漏,欢迎指正。感谢阅读!如果觉得有帮助,请点赞和分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stevedash

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值