首页 > 编程语言 >游刃有余:玩转Java泛型

游刃有余:玩转Java泛型

时间:2023-12-18 23:32:48浏览次数:31  
标签:Java List 通配符 类型 玩转 泛型 public

Java 中的泛型提供了一种创建可以处理不同类型数据的可重用代码的方法。它允许用户定义可操作各种数据类型的类、接口和方法,而无需牺牲类型安全性。在 Java 5 中引入的泛型已经成为 Java 编程语言的一个基本特性。

在 Java 引入泛型之前,它使用原始类型来允许将各种类型的对象存储在集合中。然而,这种做法存在着类型安全性不足的问题,经常导致运行时错误,也让代码变得更加难以理解和维护。泛型的出现解决了这些问题,它通过在编译时进行类型检查和类型推断来确保类型安全性,让代码更加可靠、清晰并且易于维护。

以下是 Java 中与泛型相关的一些关键概念:

  1. 类型参数:泛型使用类型参数,这些参数是使用泛型类、接口或方法时指定的类型的占位符。类型参数括在尖括号( <> 符号)中,并且可以随意命名。常见约定包括使用单个大写字母(例如,E、T、K、V)。
  2. 泛型类和接口:可以通过在其声明中包含类型参数来定义泛型类或接口。然后,这些参数可以用作类或接口中的字段类型、方法参数和返回类型。创建泛型类或接口的实例时,将提供类型参数来指定所使用的实际类型。
  3. 类型界限:可以通过指定类型界限来约束可用作泛型类或接口的参数的类型。类型界限可以是特定的类或接口,它们确保只有扩展指定类或实现指定接口的类型才能用作类型参数。
  4. 通配符:通配符在处理未知类型时提供了灵活性。Java 中有两种通配符类型:上界通配符 ( ? extends Type) 和下界通配符 ( ? super Type)。上界通配符允许作为指定类型的子类型的任何类型,而下界通配符允许作为指定类型的超类型的任何类型。
  5. 泛型方法:除了泛型类和接口之外,Java 还支持泛型方法。这些方法有自己的类型参数,可用于指定其参数的类型并独立于封闭类或接口返回值。

泛型带来了许多好处,比如提高了类型安全性、促进了代码重用,并且能让代码更加简洁。它们使您能够编写更通用的算法和数据结构,可以处理多种类型,同时保持了编译时的类型检查。借助泛型,您能够创建更为健壮且易于维护的 Java 代码。

Java 泛型的优点

Java 泛型提供了多个优点,有助于编写更安全、更灵活和更可重用的代码。以下是 Java 泛型的一些主要优点:

  1. 类型安全:泛型的主要好处之一是提高类型安全性。通过泛型,开发者可以指定类、接口或方法可以使用的元素类型。这使得编译器能够在编译时执行类型检查,防止与类型相关的错误并促进更可靠的代码。它消除了显式类型转换的需要,并降低了运行时 ClassCastException 的风险。
  2. 代码可重用性:泛型允许开发者编写可在不同类型上运行的可重用代码。通过使用类型参数对类、接口和方法进行参数化,可以创建可与各种数据类型一起使用的组件。这可以促进代码重用,因为开发者不必为不同类型重写类似的代码。相反可以创建适用于多种类型的通用算法和数据结构。
  3. 编译时类型检查:使用泛型使编译器能够执行编译时类型检查,在代码执行之前捕获类型错误。这有助于及早发现类型不匹配,从而更轻松地在开发过程中识别和修复问题。通过在编译时识别与类型相关的错误,可以降低在运行时遇到与类型相关的错误的可能性。
  4. 增强的可读性和可维护性:泛型通过明确指示预期类型来提高代码可读性。通过使用类型参数,开发者可以向其他开发人员传达代码的期望,从而使其更易于理解和维护。它还减少了对注释或文档来解释变量、参数和返回值的目的和预期类型的​​需要。
  5. 性能优化:Java 中的泛型是使用类型擦除来实现的。这意味着类型信息在运行时被删除,编译后的代码可以使用原始类型。因此,不会因泛型而产生运行时开销。这允许编写通用代码而不牺牲性能。
  6. 集合安全:泛型大大增强了ArrayList、LinkedList、HashMap等集合的安全性和完整性。使用泛型,开发者可以指定存储在集合中的对象的类型,并且编译器确保仅插入或检索指定类型的对象。这可以防止运行时错误,提高代码可靠性,并避免在使用集合时进行显式类型转换。

总的来说,Java 泛型在多个方面都带来了明显的优势,包括类型安全、代码重用、可读性、可维护性以及集合安全性。它们让能够编写健壮且灵活的代码,减少了与类型相关的错误,并且有助于促进更优秀的软件工程实践。

Java泛型示例程序

下面是一个示例程序,演示了 Java 中泛型的使用:

/**  
 * GenericDemo类, 用于演示泛型的使用  
 * @param <F>  
 */  
public class GenericDemo<F> {  
  
    private F value;  
  
    public GenericDemo(F value) {  
        this.value = value;  
    }  
  
    public F getValue() {  
        return value;  
    }  
  
    public void setValue(F value) {  
        this.value = value;  
    }  
  
    public static void main(String[] args) {  
        GenericDemo<String> stringDemo = new GenericDemo<>("Hello, FunTester!");  
        System.out.println("String demo: " + stringDemo.getValue());  
        GenericDemo<Integer> integerDemo = new GenericDemo<>(42);  
        System.out.println("Integer demo: " + integerDemo.getValue());  
    }  
  
}

在此示例中,我们有一个名为 的泛型类GenericExample,它可以与任何类型一起使用T。它有一个value类型为 的私有字段T,以及用于操作该值的构造函数、getter 和 setter 方法。

在该main方法中,我们创建了两个实例GenericExample:一个具有类型参数String,另一个具有类型参数Integer。我们使用不同的值初始化它们,然后使用该getValue方法检索并打印这些值。

当我们运行程序时,它会输出:

String demo: Hello, FunTester!
Integer demo: 88

该类GenericExample是用不同的类型(StringInteger)实例化的,并且无论类型如何,代码都保持不变。这演示了泛型如何允许我们编写可用于不同类型的可重用代码。

使用不同类型的 Java 泛型示例

以下是一些展示 Java 中不同类型泛型的示例:

多种类型的通用方法

public static <K, V> void printMap(Map<K, V> map) {  
    for (Map.Entry<K, V> entry : map.entrySet()) {  
        System.out.println(entry.getKey() + " : " + entry.getValue());  
    }  
}  
  
public static void main(String[] args) {  
    Map<String, Integer> users = new HashMap<>();  
    users.put("张三", 27);  
    users.put("李四", 30);  
    users.put("王武", 33);  
    printMap(users);  
}

在此示例中,我们有一个通用方法printMap,可以采用Map任何键值类型。该方法迭代映射条目并打印它们。在该main方法中,我们创建一个Map包含String键和Integer值的对象并将其传递给该printMap方法。

有界限的通用类

public class NumerGeneric<F extends Number> {

    private F value;

    public NumerGeneric(F value) {
        this.value = value;
    }

    public double square() {
        double num = value.doubleValue();
        return num * num;
    }

    public static void main(String[] args) {
        NumerGeneric<Integer> intBox = new NumerGeneric<>(5);
        System.out.println("Square of Integer: " + intBox.square());

        NumerGeneric<Double> doubleBox = new NumerGeneric<>(3.14);
        System.out.println("Square of Double: " + doubleBox.square());
    }

}

在此示例中,我们有一个泛型类NumerGeneric,它仅接受扩展的类型Number。它有一个初始化值的构造函数和一个square计算值平方的方法。在该main方法中,我们创建NumerGenericwithIntegerDoubletypes 的实例,然后调用该square方法。

通用接口

/**
 * 数组实现的栈
 * @param <F>
 */
public class ArrayStack<F> extends Stack<F> {
    private List<F> stack = new ArrayList<>();

    /**
     * push一个元素到栈顶
     * @param element   the item to be pushed onto this stack.
     */
    public void push(F element) {
        stack.add(element);
    }

    /**
     * pop栈顶元素
     * @return
     */
    public F pop() {
        if (isEmpty()) {
            throw new NoSuchElementException("Stack 为空");
        }
        return stack.remove(stack.size() - 1);
    }

    /**
     * 返回栈顶元素
     * @return
     */
    public boolean isEmpty() {
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        Stack<String> stringStack = new ArrayStack<>();
        stringStack.push("Java");
        stringStack.push("is");
        stringStack.push("Fun");

        while (!stringStack.isEmpty()) {
            System.out.println(stringStack.pop());
        }
    }
}

Stack在此示例中,我们使用方法pushpop和定义通用接口isEmpty。然后,我们使用一个ArrayStack使用泛型List来存储元素的类来实现该接口。在该main方法中,我们创建一个ArrayStackwithString类型的实例,并在堆栈上执行压入和弹出操作。

这些示例演示了 Java 中泛型的多功能性,允许您以泛型和类型安全的方式处理不同的类型。

Java 泛型中的通配符

Java泛型中的通配符提供了一种指定未知类型或一系列类型的方法。它们在使用泛型类、接口和方法时提高了灵活性。通配符有两种类型:上限通配符 ( ? extends Type) 和下限通配符 ( ? super Type)。

  1. 上限通配符? extends Type):上限通配符将未知类型限制为特定类型或其任何子类型。它允许您指定参数可以是扩展或实现特定类或接口的任何类型。
public static double sumOfList(List<? extends Number> numbers) {  
    double sum = 0.0;  
    for (Number number : numbers) {  
        sum += number.doubleValue();  
    }  
    return sum;  
}  
  
public static void main(String[] args) {  
    List<Integer> integers = Arrays.asList(1, 2, 3);  
    double sum = sumOfList(integers);  
    System.out.println("Sum: " + sum);  
}

在此示例中,该方法sumOfList接受List带有上限通配符的a <? extends Number>。这意味着它可以接受扩展的任何类型的列表Number,例如IntegerDoubleFloat。该方法迭代列表并计算数字的总和。

  1. 下界通配符? super Type):下界通配符将未知类型限制为特定类型或其任何超类型。它允许您指定参数可以是特定类或接口的超类或超接口的任何类型。
public static void printElements(List<? super Integer> list) {  
    for (Object element : list) {  
        System.out.println(element);  
    }  
}  
  
public static void main(String[] args) {  
    List<Number> numbers = new ArrayList<>();  
    numbers.add(10);  
    numbers.add(20L);  
    numbers.add(30.5);  
    printElements(numbers);  
}

在此示例中,该方法printElements接受List带有下限通配符的a <? super Integer>。这意味着它可以接受任何类型的超类列表Integer,例如NumberObject。该方法迭代列表并打印每个元素。

当您有需要对未知类型或一系列类型进行操作的通用代码时,通配符可以提供灵活性。它们允许您通过容纳不同的类型来编写更通用和可重用的代码,而无需牺牲类型安全性。

  1. 无界通配符?):Java 泛型中的无界通配符,仅用问号表示?,通过接受任何类型来实现最大的灵活性。当你想要使用泛型类、接口或方法而不指定任何类型限制时,它非常有用。下面是一个演示无界通配符用法的示例:
public static void printList(List<?> list) {  
    for (Object element : list) {  
        System.out.println(element);  
    }  
}  
  
public static void main(String[] args) {  
    List<Integer> integers = new ArrayList<>();  
    integers.add(1);  
    integers.add(2);  
    integers.add(3);  
  
    List<String> strings = new ArrayList<>();  
    strings.add("Hello");  
    strings.add("World");  
  
    printList(integers);  
    printList(strings);  
}

在此示例中,我们有一个名为 的方法printList,它接受List带有无界通配符的a List<?>。这意味着该方法可以接受List任何类型的 a。

在该main方法中,我们创建两个List实例 - 一个具有Integer类型,另一个具有String类型。然后我们调用该printList方法并传入这些列表。该方法迭代列表的元素并打印它们。

通过使用无界通配符,该printList方法变得通用并且可以处理List任何类型的实例。这允许最大的灵活性,因为它接受和处理列表而对元素类型没有任何限制。

总体而言,Java泛型为开发者带来了显著的优势,使得代码更加安全、灵活、可维护,并促进了更好的软件工程实践。

标签:Java,List,通配符,类型,玩转,泛型,public
From: https://blog.51cto.com/FunTester/8879348

相关文章

  • Java面向对象程序设计(上海交通大学出版社)12章及以后的课后问题解析
    1)Map集合和Collection集合的区别是什么? Map集合和Collection集合都是Java集合框架中的接口,它们之间有一些关键的区别:元素存储方式:Collection:用于存储单一元素的集合接口。它继承自Iterable接口,包含常见的子接口如List、Set。Map:用于存储键值对(key-value......
  • JavaScript 中 let、var 和 const 的区别及使用建议
    前言JavaScript中的let、var和const是三种不同的变量声明方式。虽然它们都可以用来声明变量,但它们之间有很大的区别。在本篇文章中,我们将深入探讨这三种变量声明方式的区别以及它们在实际开发中的应用。正文内容一、let的用法let是ES6中新增的变量声明方式,它的作用域......
  • 无涯教程-Java - SortedSet 集合接口函数
    SortedSet接口扩展了Set并声明了按升序排序的集合的行为。除了Set定义的那些方法外,SortedSet接口还声明了下表中概述的方法-如果尝试使用null对象并且集合中不允许使用null,则抛出NullPointerException。Sr.No.Method&Remark1Comparatorcomparator()返回调用排序集的比......
  • java基础语法之二维数组1
    一:概述在前面的博文中,已经说明了一维数组相关的基础知识和案例,接下来就是对二维数组的介绍。首先介绍二维数组的相关基础介绍。二:具体说明二维数组:元素为一维数组的数组。<1>二维数组的定义格式数据类型[][]变量名; int[][]arr; 数据类型变量名[][]; intarr[][];数据类型[]......
  • Java异常处理神器:Guava Throwables类
    第一章:Guava库简介Guava由Google开发,它提供了大量的核心Java库,例如:集合、缓存、原生类型支持、并发库、通用注解、字符串处理和I/O操作等。这些功能在日常的Java开发中超级常用,而且Guava的设计哲学是简洁高效,这让咱们的代码不仅更加优雅,而且更加易于维护和阅读。尤其是在异常处......
  • Java第十二课_常用类
    基本数据类packagecom.msr.lesson01;importorg.junit.Test;//测试单元JUnit4.12:测试时无需使用main函数.在@Test中不需要main函数,且函数可以一个个测.//注意:被测试函数不能有返回值也不能有形参,并且必须public修饰publicclassPractice{@T......
  • Java登陆第二十五天——Tomcat、认识JavaWeb项目
    Java项目开发后,需要部署到服务器中,服务器需要有最基本的操作系统。单一的操作系统还不够,因为Java项目经过JVM编译后的是.class文件(字节码文件)。字节码文件的运行需要Java运行环境(JRE)。有了JRE还是不够。不是所有的项目都可以直接运行,还需要服务器软件服务器软......
  • 无涯教程-Java - Set 集合接口函数
    Set集合是不能包含重复元素的集合,Set接口仅包含从Collection继承的方法,并增加了禁止重复元素的限制。下表总结了Set声明的方法-Sr.No.Method&Remark1add()将对象添加到集合中。2clear()从集合中删除所有对象。3contains()如果指定对象是集合中的元素,则返回t......
  • JavaScript高频题整理(附答案背诵版)
    1、简述JavaScript中map和foreach的区别?map和forEach都是JavaScript数组的迭代方法,但它们之间存在一些关键区别。返回值:map方法会返回一个新的数组,这个新数组是由原数组通过某个函数处理后的结果组成的。而forEach方法则没有返回值,它仅仅是对数组中的每个元素执行一次给定的函......
  • 无涯教程-Java - Collection 接口函数
    Collection接口是构建收集框架的基础。它声明了所有集合将拥有的核心方法。下表总结了这些方法。Sr.No.Method&Remark1booleanadd(Objectobj)将obj添加到调用集合中。如果将obj添加到集合中,则返回true。如果obj已经是集合的成员,或者该集合不允许重复,则返回false。2......