首页 > 编程语言 >Java基础夯实——1. 6 Java泛型

Java基础夯实——1. 6 Java泛型

时间:2024-11-15 20:51:46浏览次数:3  
标签:Java List value 夯实 接口 类型 泛型 public

Java的泛型概念

Java的泛型(Generics是一种参数化类型的机制。它允许在定义类、接口和方法时使用类型参数,这些类型参数可以在使用该类、接口或方法时被具体的类型所替换。

示例:

// 一个简单的泛型类
public class Box<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

在上述代码中泛型类Box中T就是一个类型参数。这个类可以用来存储不同类型的对象,如Box可以用来存储整数,Box可以用来存储字符串。

Java的泛型提供了强大的功能,能够在类、接口和方法中使用类型参数。

1. 泛型类

泛型类是指在类定义时使用类型参数,这使得类可以处理不同类型的对象,而不需要为每个类型定义不同的类。

示例代码:

// 定义一个泛型类 Box
public class Box<T> {
    private T value;

    // 设置值
    public void setValue(T value) {
        this.value = value;
    }

    // 获取值
    public T getValue() {
        return value;
    }

    public static void main(String[] args) {
        // 使用泛型类
        Box<Integer> intBox = new Box<>();
        intBox.setValue(10);
        System.out.println("Integer value: " + intBox.getValue());

        Box<String> strBox = new Box<>();
        strBox.setValue("Hello, World!");
        System.out.println("String value: " + strBox.getValue());
    }
}

解释:

在这个例子中,Box是一个泛型类,T代表类型参数,表示Box类可以容纳任何类型的对象。T的类型可以在创建实例时指定。
Box指定T为Integer类型,而Box则指定T为String类型。
setValue和getValue方法通过泛型参数T确保类型安全,可以避免类型转换错误。

总结:

泛型类使得类可以适应不同类型的数据,而不需要重复定义多个类。
通过使用泛型,可以在编译时确保类型安全,避免运行时出现类型转换错误。

2. 泛型接口

泛型接口是指接口声明中包含类型参数,可以使得实现该接口的类在使用时指定类型。泛型接口允许接口方法操作不同类型的对象。

示例代码:

// 定义一个泛型接口
public interface Pair<K, V> {
    K getKey();
    V getValue();
}

// 实现泛型接口
public class SimplePair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public SimplePair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public static void main(String[] args) {
        // 使用泛型接口
        Pair<String, Integer> pair = new SimplePair<>("age", 30);
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
    }
}

解释:

Pair<K, V>是一个泛型接口,具有两个类型参数K和V,分别表示键和值的类型。
SimplePair<K, V>是Pair接口的实现类,它实现了getKey和getValue方法。
在使用时,创建了一个Pair<String, Integer>的实例,这表明键的类型是String,值的类型是Integer。

总结:

泛型接口使得接口可以定义与类型相关的操作,从而提高代码的灵活性。
泛型接口的使用允许为不同的类型定义通用接口,而不需要为每个类型编写不同的接口实现。

3. 泛型方法

泛型方法是在方法定义时使用类型参数,使得该方法能够处理不同类型的参数。与泛型类和接口不同,泛型方法只在方法签名中声明类型参数。

// 定义一个泛型方法
public class GenericMethodExample {
    
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        // 调用泛型方法
        Integer[] integers = {1, 2, 3};
        String[] strings = {"Hello", "World"};
        
        printArray(integers); // 输出整数数组
        printArray(strings);  // 输出字符串数组
    }
}

解释:

printArray是一个泛型方法,它使用了类型参数T,使得该方法能够接受任何类型的数组。
在调用时,Integer[]和String[]数组被作为参数传递给方法,T分别被替换为Integer和String类型。
该方法遍历数组并打印每个元素。

总结:

泛型方法使得一个方法能够处理不同类型的参数,而不需要为每种类型编写不同的方法。
它通常与泛型类或接口一起使用,提供更大的灵活性和代码复用性。

总之

泛型类允许类定义时使用类型参数,这使得类可以操作多种类型的对象,避免了类型转换和重复代码的编写。
泛型接口让接口方法能支持不同类型的实现,使得代码更加灵活且可扩展。
泛型方法使得方法能在不依赖于类或接口的类型情况下,接受不同类型的参数,提高了代码的通用性。

4. 通配符(Wildcard)

在泛型中,通配符用于表示不特定的类型。通配符允许编写更加通用的代码,尤其是在处理泛型集合时,它能够提高灵活性,同时保持类型安全。常见的通配符有以下三种:? extends T(上界通配符),? super T(下界通配符),和?(无界通配符)。

4.1 ? extends T:上界通配符

? extends T 表示类型必须是 TT 的子类。通过上界通配符,可以确保在传递参数时,元素类型为 T 或其子类,但无法往集合中添加元素,因为无法确定具体的类型(可能是 T 或其子类)。

示例代码:

public void printNumbers(List<? extends Number> numbers) {
    for (Number number : numbers) {
        System.out.println(number); // 可以读取,但不能修改
    }
}

解释:

  • List<? extends Number> 表示一个列表,它的元素类型可以是 Number 或者 Number 的任何子类,比如 IntegerDoubleFloat 等。
  • 在方法内部,您可以安全地读取 Number 类型的元素(for 循环中),但是无法往列表中添加元素,因为不知道具体的类型(可能是 IntegerDouble 等)。例如,不能执行 numbers.add(1),因为 numbers 可能是 List<Integer>List<Double>IntegerDouble 是不同的类型。

4.2 ? super T:下界通配符

? super T是一种通配符类型,被称为下界通配符。它代表的是一个未知类型,这个未知类型是T或者是T的超类型(父类型)。例如,如果TInteger,那么? super Integer可以是NumberInteger的父类型)或者Object(所有类的父类型)等。
使用? super T通配符时,从这个集合中读取元素会受到限制。因为编译器不确定集合中的元素具体是T的哪种超类型,读取出来的元素在编译时会被当作Object类型来处理。

示例代码:

public static <T> void addElements(List<? super T> list, T element) {
    list.add(element);
}
  • 在这个方法中,List<? super T>表示这个列表的元素类型是T或者T的超类型。这样就可以保证T类型的element能够安全地添加到这个列表中。

4.3 无限制通配符 ?:可以是任何类型

? 是最通用的通配符,它表示可以接受任何类型的元素。使用这种通配符时,通常只能进行读取操作,不能进行修改操作。

示例代码:

public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj); // 只能读取元素作为 Object 类型
    }
}

解释:

  • List<?> 表示一个未知类型的列表,元素类型可以是任何类型。它允许处理任何类型的列表,但由于不知道具体的类型,因此只能读取元素(它们会被视为 Object 类型),而不能往列表中添加元素。
  • 例如,无法执行 list.add("String"),因为不知道该列表的具体类型。

5. 泛型的边界(Bounds)

在泛型中,边界用于限制类型参数的范围。通过设置上界(extends)和下界(super),可以让泛型更加灵活和安全。泛型的边界可以限制一个泛型类型只能是某个类或接口的子类或父类,从而更精确地控制类型。

上界限制(extends

上界限制通过 extends 关键字指定,表示类型参数必须是某个类或接口的子类。通常用于读取数据时,只允许该类型及其子类作为有效类型。

示例代码:

public <T extends Number> void printNumber(T number) {
    System.out.println(number);
}

解释:

  • T extends Number 表示 T 必须是 Number 类型或其子类(如 IntegerDouble 等)。
  • 在该方法中,能够安全地读取并打印 T 类型的值,因为 T 确保了它是 Number 或其子类的实例。
  • 上界限制用于方法中需要读取某些特定类型时非常有用,避免了直接使用 Object 类型的做法。

下界限制(super

下界限制通过 super 关键字指定,表示类型参数必须是某个类或接口的父类。通常用于写入数据时,只允许该类型及其父类作为有效类型。

示例代码:

public <T> void addNumbers(List<? super T> list) {
    list.add(1);  // 可以安全地添加 T 或其子类的元素
}

解释:

  • ? super T 表示可以向该集合中添加 T 类型或其父类的类型(例如 Object)。
  • 下界限制常用于需要向集合中添加元素时,保证可以添加指定类型及其父类的元素。

6. 泛型的类型擦除

Java的泛型使用类型擦除技术,在编译时将所有泛型类型转换为原始类型(通常是 Object 或指定的上界)。

示例代码:

List<String> list = new ArrayList<>();
list.add("Hello");
// 在运行时,编译后的代码不会区分 List<String> 和 List<Integer>

解释:

  • 在编译时,List<String> 被擦除为 List,这意味着编译后不再保留类型参数 String
  • 由于类型擦除,Java 会在编译时移除泛型信息,并将 List<String>List<Integer> 都当作普通的 List 类型处理。这也解释了为什么在运行时无法直接访问泛型类型。
  • 由于擦除,Java 不能直接获取泛型类型,因此像 List<String>List<Integer> 在运行时都表现为 List,因此不能直接进行类型判断或类型转换。

7. 泛型的应用场景

常见的应用场景包括:

  • List:表示一个元素为 T 类型的列表。
  • Map<K, V>:表示键值对集合,其中键的类型是 K,值的类型是 V
  • Set:表示一个元素为 T 类型的集合,集合中的元素是唯一的。
  • Queue:表示一个元素为 T 类型的队列。
  • Optional:用于表示一个可能为空的值,Optional 存储类型为 T 的元素。

常见问题

1. Java中泛型是什么?使用泛型的好处是什么?

Java 泛型 是一种通过类型参数化的机制,允许在类、接口和方法中定义类型参数。通过泛型,Java 可以在编译时确保类型安全,避免出现类型转换错误。

2. 常用泛型标记有哪些?

  1. T:表示任意类型(通常用于类或方法中的类型参数)。例如,T 代表 “Type”。

    • 示例:public <T> void print(T value) {...}
  2. E:表示元素类型,常用于集合类中。

    • 示例:public interface List<E> {...}
  3. K:表示键类型,常用于 Map 类中。

    • 示例:public interface Map<K, V> {...}
  4. V:表示值类型,常用于 Map 类中。

    • 示例:public interface Map<K, V> {...}
  5. N:表示数字类型,通常用于表示数值范围。

    • 示例:public <N extends Number> void sum(N num) {...}
  6. : 表示不确定的Java类型。

3. 什么是类型擦除?

类型擦除是指,在编译时Java 将泛型类型转换成原始类型(通常是 Object 或指定的上界),并移除类型参数。这意味着泛型信息在运行时不可用。

示例:

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // 输出 true

解释:

  • 在编译时,List<String>List<Integer> 会都被转换成 List。这就意味着在运行时,List<String>List<Integer> 都会被视为相同类型 List,因此它们的 getClass() 方法返回的是相同的类对象。

4. 如何在类上使用泛型?

示例:泛型类

// 定义一个泛型类 Box
public class Box<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.setValue(10);
        System.out.println("Integer value: " + intBox.getValue());

        Box<String> strBox = new Box<>();
        strBox.setValue("Hello, World!");
        System.out.println("String value: " + strBox.getValue());
    }
}

标签:Java,List,value,夯实,接口,类型,泛型,public
From: https://blog.csdn.net/m0_60235638/article/details/143753328

相关文章

  • 【Java的JIT技术】
    JIT(JustInTime)编译器,也称即时编译器,主要在JVM运行时期动态的做一些优化提升代码的运行速度和效率。大家都知道,jvm运行时是通过解释器将每一条class字节码指令翻译成机器指令去执行。JIT则不同,它会在jvm运行时期动态的将热点代码编译成机器指令,缓存下来.之......
  • Springboot 整合 Java DL4J 打造金融风险评估系统
    ......
  • 大数据学习15之Scala集合与泛型
    1.概述        大部分编程语言都提供了数据结构对应的编程库,并称之为集合库(CollectionLibrary),Scala也不例外,且它还拥有以下优点:易用:灵活组合运用集合库提供的方法,可以解决大部分集合问题简洁:拜类型推断和函数式编程所赐,帮助程序员写出更简洁,更优雅的代码安全:......
  • LeetCode100之两数相加(2)--Java
    1.问题描述        给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之外,这两个数都不会以0 开头。     ......
  • Java 21和Java 8在洛谷上的区别
    Java21默认开大内存很容易遇到所以如果换成Java8最后一个我也不知道为啥,有大佬帮忙看一下吗逆序对-洛谷importjava.util.*;publicclassMain{  staticScannercin=newScanner(System.in);  //非递归版本的归并排序,返回逆序对的数量  publics......
  • JavaFX史上最全教程 - Shape - JavaFX路径
    JavaFX有其他内置的形状,如ArcCircleCubicCurveEllipseLinePathPolygonPolylineQuadCurveRectangleSVGPathText以下代码显示了如何创建路径。importjavafx.application.Application;importjavafx.geometry.Insets;importjavafx.scene.Group;importjavafx.scene.Scene;......
  • Loom开篇:Java 虚拟机的协程和延续
    在jdk19发布的时候,java推出了一种全新的线程模型。说是全新的也只是针对java自己而言的。实际上其他语言早就有了类似的实现。这个东西其实就是协程,在java中叫做虚拟线程。jdk中虚拟线程主要是projectloom(以下称为loom)实现的。本文我们针对一篇jdk博客的翻译来打开虚拟......
  • java 反序列化 cc7 复现
    复现环境:common-collections版本<=3.2.1,java版本随意.cc7就是cc6换了一个出口,整体的逻辑没有太大的变化.在Lazymap之前的还那样,我们从如何触发Lazymap的get方法开始看起.AbstractMap看他的equals方法publicbooleanequals(Objecto){if(o==this)ret......
  • JAVA部分基础知识点(四)【持续更新】
    1.方法重写当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆善父类的这个方法,这就是方法重写。【注意:重写后,方法的访问,Java会遵循就近原则】方法重写的其它注意事项:重写小技巧:使用Override注解,他可以指定java......
  • Java概述
    1.Java程序编写和执行的过程:步骤1:编写。Java代码编写在.Java结尾的文件中。步骤2:编译。格式:javac源文件名.java步骤3:运行。格式:java字节码文件名2.编写的说明:classHelloWorld{publicstatic.void.main(String[]args){System.out.println("helloworld!!你好中国!");}......