Java 泛型:从入门到起飞

在 Java 编程的浩瀚宇宙中,泛型堪称一颗璀璨夺目的 "多面宝石"。它不仅能赋予代码卓越的通用性,极大提升安全性,更能巧妙规避大量繁琐重复的类型转换操作。接下来,就让我们一同踏上探索 Java 泛型的奇妙旅程,从基础知识逐步深入高阶应用,全方位解锁其强大潜能,让你轻松驾驭这一编程利器!

一、泛型初相识

(一)什么是泛型?

简单来说,泛型就是一种参数化类型的机制。它允许我们在定义类、接口或方法的时候,不指定具体的类型,而是用一个占位符(类型参数)来代替。这样,在使用这些类、接口或方法的时候,再传入具体的类型。

比如说,我们有一个盒子 Box 类,它可以用来装各种东西。如果没有泛型,我们可能需要为每一种要装的东西都创建一个单独的 Box 类,像装苹果的 AppleBox,装橘子的 OrangeBox 等等,这显然太麻烦了。但有了泛型,我们只需要一个 Box 类,就可以装任何类型的东西啦!

(二)泛型的语法

在 Java 中,定义一个泛型类的语法是在类名后面加上一对尖括号<>,里面写上类型参数。比如:

csharp 复制代码
class Box<T> {
    private T content;
    public void setContent(T content) {
        this.content = content;
    }
    public T getContent() {
        return content;
    }
}

这里的T就是类型参数,它可以是任何合法的标识符,通常我们会用单个大写字母来表示,常见的有T(Type 的缩写)、E(Element 的缩写,常用于集合)、K和V(Key 和 Value 的缩写,常用于映射)等。

(三)泛型的使用

有了上面定义的Box类,我们就可以像这样使用它:

ini 复制代码
Box<Integer> integerBox = new Box<>();
integerBox.setContent(10);
Integer value = integerBox.getContent();

这里我们创建了一个Box类型的对象,它只能装Integer类型的东西。通过这种方式,编译器可以在编译时就检查类型的正确性,避免了运行时的类型错误。

二、泛型的进阶之路

(一)泛型方法

除了泛型类,我们还可以定义泛型方法。泛型方法的语法是在方法返回类型前面加上<>,里面写上类型参数。比如:

php 复制代码
class Util {
    public static <T> T getFirstElement(T[] array) {
        if (array != null && array.length > 0) {
            return array[0];
        }
        return null;
    }
}

这个getFirstElement方法可以接受任何类型的数组,并返回数组的第一个元素。使用起来也很简单:

ini 复制代码
Integer[] intArray = {1, 2, 3};
Integer firstInt = Util.getFirstElement(intArray);
String[] stringArray = {"Hello", "World"};
String firstString = Util.getFirstElement(stringArray);

(二)类型通配符

类型通配符是泛型中的一个重要概念,它用?表示。类型通配符主要有两种用法:上限通配符和下限通配符。

1. 上限通配符

上限通配符的语法是<? extends 类型>,它表示这个通配符所代表的类型是某个类型的子类(包括自身)。例如:

scala 复制代码
class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
class Zoo {
    public static void printAnimals(List<? extends Animal> animals) {
        for (Animal animal : animals) {
            System.out.println(animal);
        }
    }
}

这里的printAnimals方法可以接受任何类型为Animal及其子类的列表,比如List或List。

2. 下限通配符

下限通配符的语法是<? super 类型>,它表示这个通配符所代表的类型是某个类型的父类(包括自身)。例如:

scala 复制代码
class Fruit {}
class Apple extends Fruit {}
class RedApple extends Apple {}
class FruitBasket {
    public static void addRedApple(List<? super RedApple> basket, RedApple apple) {
        basket.add(apple);
    }
}

这个addRedApple方法可以接受任何类型为RedApple及其父类的列表,比如List或List。

(三)泛型接口

泛型接口的定义和泛型类类似,也是在接口名后面加上<>和类型参数。例如:

csharp 复制代码
interface Mapper<K, V> {
    V map(K key);
}

然后我们可以创建实现这个接口的类:

vbnet 复制代码
class IntegerToStringMapper implements Mapper<Integer, String> {
    @Override
    public String map(Integer key) {
        return String.valueOf(key);
    }
}

(四)泛型的继承与多态

泛型类和接口也支持继承和多态。比如:

scala 复制代码
class Parent<T> {
    public void print(T t) {
        System.out.println(t);
    }
}
class Child<T> extends Parent<T> {
    public void printTwice(T t) {
        print(t);
        print(t);
    }
}

这里Child类继承了Parent类,并且可以使用父类的泛型类型参数T。

三、高阶泛型玩法

(一)泛型的嵌套

泛型还可以嵌套使用,比如:

ini 复制代码
List<List<Integer>> nestedList = new ArrayList<>();
List<Integer> innerList = new ArrayList<>();
innerList.add(1);
innerList.add(2);
nestedList.add(innerList);

这里nestedList是一个包含List的列表,也就是一个二维列表。

(二)泛型与反射

在反射中使用泛型可以让我们编写更加灵活和通用的代码。例如:

java 复制代码
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
class GenericClass<T> {
    private T value;
    public T getValue() {
        return value;
    }
}
public class GenericReflection {
    public static void main(String[] args) throws NoSuchFieldException {
        GenericClass<Integer> genericClass = new GenericClass<>();
        ParameterizedType genericSuperclass = (ParameterizedType) genericClass.getClass().getGenericSuperclass();
        Type[] typeArguments = genericSuperclass.getActualTypeArguments();
        System.out.println("The type argument is: " + typeArguments[0]);
    }
}

这段代码通过反射获取了GenericClass的类型参数Integer。

(三)泛型的擦除

Java 的泛型是在编译时实现的,编译后字节码中的泛型信息会被擦除,只保留原始类型。例如:

ini 复制代码
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
System.out.println(stringList.getClass() == integerList.getClass());

这段代码输出true,因为在运行时,List和List的实际类型都是ArrayList,泛型信息被擦除了。

四、总结

Java 的泛型是一个强大而又复杂的特性,它可以让我们编写更加通用、安全和高效的代码。从基础的泛型类和方法,到进阶的类型通配符、泛型接口,再到高阶的泛型嵌套、反射和擦除,每一步都为我们的编程带来了更多的可能性。希望通过这篇文章,你能对 Java 泛型有一个全面而深入的理解,并且在实际编程中能够熟练运用它,让你的代码飞起来!

相关推荐
若亦_4 分钟前
Java中关于this的三种用法
java·开发语言·intellij-idea
山甫aa6 分钟前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
软件技术NINI8 分钟前
基于SpringBoot和Vue的博物馆预约平台管理系统的设计与实现
java·毕业设计·毕设
jktantan1 小时前
注解式脱敏:用 Jackson 的 ContextualSerializer 做字段级策略(一)
spring boot·后端·算法·spring cloud·系统架构
独泪了无痕10 小时前
探索Pinyin4j-Java中的拼音转换利器
java·后端
道尔柯南11 小时前
C++ string 类详解:从常用接口到深拷贝,彻底掌握字符串处理
android·java·c++
kingx312 小时前
Bugku 3122 谷歌小恐龙题解:纯离线逆向 Vault 的 AES-GCM 加密资源(附 Python 解密器)
java·逆向·ctf·bugku·aes-gcm
DevOpenClub14 小时前
职教高考控制线 API:招生类别枚举、筛选与空分数含义
java·数据库·高考
露天赏雪15 小时前
第3篇:Sentinel熔断降级实战:大模型接口抖动时的服务兜底方案
java·开发语言·人工智能·spring boot·spring·servlet·sentinel
天天被压力15 小时前
【A股分钟级高频数据完全手册 #06】北交所分钟级数据接口:京市股票Pro版最新分时
java·人工智能·python