深入理解Java中的泛型机制

深入理解Java中的泛型机制

简单了解泛型

以前对于泛型的认知就停留在编译后会变成Object类型,对于它的优点没有进行过多的思考,以至于在前几个月的面试中被虐的是体无完肤.今天就来系统的总结一下泛型存在的意义到底是什么.

Java中泛型的存在有以下几个主要意义:

类型安全:泛型提供了编译时的类型检查机制,可以在编译阶段捕获类型错误,减少运行时出现的类型相关的异常。通过使用泛型,可以确保程序在类型上是安全的,避免了类型转换错误和潜在的ClassCastException。

List<String> names = new ArrayList<>();  // 使用泛型声明一个只能存储字符串的列表
names.add("Alice");
names.add(123);  // 编译时错误,类型不匹配

在上述示例中,通过使用泛型限定列表只能存储字符串类型的元素,编译器会在添加元素时进行类型检查,如果尝试添加非字符串类型的元素,编译时就会发现错误,提供了类型安全性。

代码复用:泛型使得代码更加通用和可重用。可以编写泛型类和方法,能够适用于多种数据类型,而不需要针对每种类型编写重复的代码。通过泛型,可以实现更高效和简洁的代码。

public class Pair<T, U> {
    private T first;
    private U second;
    
    public Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
    
    public T getFirst() {
        return first;
    }
    
    public U getSecond() {
        return second;
    }
}

// 使用泛型的Pair类可以适用于不同类型的数据
Pair<String, Integer> pair1 = new Pair<>("Alice", 25);
Pair<String, String> pair2 = new Pair<>("John", "Doe");

在上述示例中,通过使用泛型类 Pair,我们可以创建可以存储不同类型数据的对象。这样可以避免为每个不同类型的数据编写单独的类,实现了代码的复用和通用性。

简化API设计:通过使用泛型,可以设计更加通用和灵活的API接口。例如,Java集合框架中的泛型容器(如List、Set、Map)可以在不同场景下适应各种类型的数据,提供一致的接口和操作。

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);

int sum = numbers.stream().mapToInt(Integer::intValue).sum();

在上述示例中,通过使用泛型的集合类 List<Integer>,我们可以直接向列表中添加整数类型的元素。在使用流操作计算列表元素的和时,不需要手动进行类型转换,而是直接使用整数类型的方法,简化了API的设计和使用。

提高代码可读性和可维护性:泛型可以增加代码的可读性和可维护性。通过使用泛型,可以在代码中清晰地表达和约束数据类型的关系,使得代码更易理解和维护。

public <T extends Comparable<T>> T getMax(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

// 使用泛型方法获取最大值
int maxInt = getMax(10, 20);
double maxDouble = getMax(3.14, 2.5);

在上述示例中,通过使用泛型方法 getMax(),我们可以在不同的数据类型上获取最大值。通过泛型方法的参数类型和返回类型的一致性,代码可读性更高,更易于理解和维护。

避免类型强制转换:使用泛型可以避免手动进行类型强制转换的繁琐和容易出错。编译器会自动进行类型推断和转换,减少了手动转换的代码和潜在的类型转换错误。

List rawList = new ArrayList();  // 声明一个未指定泛型类型的原始列表
rawList.add("Alice");

String name = (String) rawList.get(0);  // 需要进行强制类型转换

List<String> 此回答由于中途截断,以下是接上的完整回答。

```java
List<String> typedList = new ArrayList<>();  // 使用泛型声明一个指定类型的列表
typedList.add("Alice");

String name = typedList.get(0);  // 无需进行强制类型转换

在上述示例中,使用原始列表时,需要进行强制类型转换以将元素转换为所需的类型。而使用带有泛型的列表时,无需进行强制类型转换,编译器自动推断并进行类型检查,避免了类型转换错误的风险。

“奇门邪术”–绕过类型检查

既然泛型存在的意义有一个可以类型检查,使得错误的程序被扼杀在摇篮中,那么就有一起奇奇怪怪的方法来绕开它,就需要依赖一种强大的机制–反射.

 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 定义一个集合,存储Integer的类型
        final ArrayList<Integer> list = new ArrayList<Integer>();
        //通过反射机制获取list的Class对象
        final Class<? extends ArrayList> aClass = list.getClass();
        //通过Class对象获取Method(实例方法类)
        final Method add = aClass.getDeclaredMethod("add", Object.class);
        //设置权限检查为不检查
        add.setAccessible(true);
        //调用该实例方法
        add.invoke(list,"hello");
        add.invoke(list,"world");
        add.invoke(list,"Java");
        System.out.println(list);
        //输出:[hello, world, Java]
    }

在上面的代码中,因为反射是一种运行时动态获取类,方法的一直机制,所以可以看到final Method add = aClass.getDeclaredMethod(“add”, Object.class);这一行代码中的字节码文件中的类型参数是Object.class,而彼时Integer.class,这也正好说明了泛型的类型擦除机制.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WindFall1314

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

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

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

打赏作者

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

抵扣说明:

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

余额充值