当前位置: 首页 > news >正文

Java 泛型技术详解

文章目录

    • 概述
    • 泛型的基本概念
      • 类型参数化
      • 类型擦除机制
    • 泛型类型的定义
      • 泛型类
      • 泛型接口
      • 泛型方法
    • 通配符与边界
      • 上界通配符(Upper Bounded Wildcards)
      • 下界通配符(Lower Bounded Wildcards)
      • 无界通配符(Unbounded Wildcards)
    • 泛型的高级特性
      • 类型边界
      • 泛型继承
    • 最佳实践
      • PECS 原则
      • 避免原始类型
      • 合理使用类型推断
    • 常见问题与解决方案
      • 泛型数组的限制
      • 类型擦除的影响


概述

Java 泛型(Generics)是 Java 5 引入的一项重要特性,它提供了编译时类型安全检查机制,能够在编译期间检测到非法的类型转换。泛型允许在定义类、接口和方法时使用类型参数,从而提高代码的类型安全性和可重用性。

泛型的基本概念

类型参数化

泛型的核心思想是类型参数化,即将类型作为参数传递给类、接口或方法。通过使用尖括号 <> 来指定类型参数,我们可以创建适用于不同数据类型的可重用代码。

// 泛型类示例
public class Box<T> {private T content;public void set(T content) {this.content = content;}public T get() {return content;}
}// 使用泛型类
Box<String> stringBox = new Box<>();
stringBox.set("Hello World");
String content = stringBox.get(); // 无需类型转换

类型擦除机制

Java 泛型采用类型擦除(Type Erasure)机制实现向后兼容性。在编译时,编译器会移除所有泛型类型信息,并在必要时插入类型转换代码。这意味着在运行时,泛型类型信息不可用。

List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();// 运行时,两个列表的类型相同
System.out.println(stringList.getClass() == intList.getClass()); // true

泛型类型的定义

泛型类

泛型类允许在类定义中使用类型参数,使类能够适用于多种数据类型。

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; }
}

泛型接口

泛型接口的定义方式与泛型类相似,为接口提供类型参数化能力。

public interface Comparable<T> {int compareTo(T other);
}public class Person implements Comparable<Person> {private String name;private int age;@Overridepublic int compareTo(Person other) {return Integer.compare(this.age, other.age);}
}

泛型方法

泛型方法允许在方法级别定义类型参数,提供更细粒度的类型控制。

public class Utility {public static <T> void swap(T[] array, int i, int j) {T temp = array[i];array[i] = array[j];array[j] = temp;}public static <T extends Comparable<T>> T max(T x, T y) {return x.compareTo(y) > 0 ? x : y;}
}

通配符与边界

上界通配符(Upper Bounded Wildcards)

使用 extends 关键字设置类型参数的上界,限制类型参数必须是指定类型或其子类型。

public void processNumbers(List<? extends Number> numbers) {for (Number num : numbers) {System.out.println(num.doubleValue());}
}// 可以接受 List<Integer>, List<Double>, List<Float> 等
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
processNumbers(intList);
processNumbers(doubleList);

下界通配符(Lower Bounded Wildcards)

使用 super 关键字设置类型参数的下界,限制类型参数必须是指定类型或其超类型。

public void addNumbers(List<? super Integer> list) {list.add(1);list.add(2);list.add(3);
}// 可以接受 List<Integer>, List<Number>, List<Object> 等
List<Number> numberList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
addNumbers(numberList);
addNumbers(objectList);

无界通配符(Unbounded Wildcards)

使用 ? 表示可以匹配任何类型的通配符。

public void printList(List<?> list) {for (Object item : list) {System.out.println(item);}
}

泛型的高级特性

类型边界

类型边界允许对类型参数施加约束,确保类型参数具有特定的属性或方法。

public class NumberProcessor<T extends Number & Comparable<T>> {public T process(T value1, T value2) {// 可以调用 Number 的方法double doubleValue = value1.doubleValue();// 可以调用 Comparable 的方法int comparison = value1.compareTo(value2);return comparison > 0 ? value1 : value2;}
}

泛型继承

泛型类型之间的继承关系需要特别注意,子类型关系不会自动传递到泛型类型。

// 错误的假设:List<String> 不是 List<Object> 的子类型
// List<String> stringList = new ArrayList<>();
// List<Object> objectList = stringList; // 编译错误// 正确的做法是使用通配符
List<String> stringList = new ArrayList<>();
List<? extends Object> objectList = stringList; // 正确

最佳实践

PECS 原则

PECS(Producer Extends, Consumer Super)原则指导我们何时使用上界和下界通配符。如果需要从泛型容器中读取数据,使用上界通配符;如果需要向泛型容器中写入数据,使用下界通配符。

// Producer: 从集合中读取数据
public static double sum(List<? extends Number> numbers) {double total = 0.0;for (Number num : numbers) {total += num.doubleValue();}return total;
}// Consumer: 向集合中写入数据
public static void fillWithNumbers(List<? super Integer> list, int count) {for (int i = 0; i < count; i++) {list.add(i);}
}

避免原始类型

始终使用参数化类型而非原始类型,以获得编译时类型安全性。

// 不推荐:使用原始类型
List rawList = new ArrayList();
rawList.add("String");
rawList.add(123);// 推荐:使用参数化类型
List<String> stringList = new ArrayList<>();
stringList.add("String");
// stringList.add(123); // 编译错误

合理使用类型推断

Java 7 引入的钻石操作符 <> 可以简化泛型实例化过程。

// Java 7 之前
Map<String, List<String>> map = new HashMap<String, List<String>>();// Java 7 及之后
Map<String, List<String>> map = new HashMap<>();

常见问题与解决方案

泛型数组的限制

Java 不支持创建泛型数组,但可以使用替代方案。

// 错误的做法
// List<String>[] arrays = new List<String>[10]; // 编译错误// 正确的替代方案
@SuppressWarnings("unchecked")
List<String>[] arrays = new List[10];
for (int i = 0; i < arrays.length; i++) {arrays[i] = new ArrayList<>();
}// 或者使用 ArrayList
List<List<String>> listOfLists = new ArrayList<>();

类型擦除的影响

由于类型擦除,某些操作在运行时不可用。

public class GenericClass<T> {// 错误:无法使用 instanceof 检查参数化类型// public boolean isInstance(Object obj) {//     return obj instanceof T; // 编译错误// }// 错误:无法创建参数化类型的数组// public T[] createArray(int size) {//     return new T[size]; // 编译错误// }// 正确的做法:使用 Class 对象private Class<T> type;public GenericClass(Class<T> type) {this.type = type;}public boolean isInstance(Object obj) {return type.isInstance(obj);}@SuppressWarnings("unchecked")public T[] createArray(int size) {return (T[]) Array.newInstance(type, size);}
}

相关文章:

  • 【判断既约分数】2022-4-3
  • JDK21深度解密 Day 13:性能调优实战案例:高并发系统与内存密集型应用的优化秘籍
  • 【数据结构初阶】--算法复杂度的深度解析
  • Linux编程:2、进程基础知识
  • 后端下载限速(redis记录实时并发,bucket4j动态限速)
  • 如何在 Java 中优雅地使用 Redisson 实现分布式锁
  • 【Redis系列 04】Redis高可用架构实战:主从复制与哨兵模式从零到生产
  • 在Vue或React项目中使用Tailwind CSS实现暗黑模式切换:从系统适配到手动控制
  • [逆向工程] C实现过程调试与钩子安装(二十七)
  • win10环境配置-openpose pytorch版本
  • 【Hugging Face】实践笔记:Pipeline任务、BERT嵌入层、Train任务、WandB解析
  • 编程基础:执行流
  • 快速幂求逆元板子
  • 【论文阅读笔记】《A survey on deep learning approaches for text-to-SQL》
  • 《高等数学》(同济大学·第7版)第二章第五节“函数微分“
  • Java IO流完全指南:从基础到进阶的全面解析
  • python打卡day47@浙大疏锦行
  • 【手动触发浏览器标签页图标自带转圈效果】
  • vue3: bingmap using typescript
  • mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
  • 旅行社b2b网站建设方案/全国人大常委会副委员长
  • 哪些是网站建设/品牌营销策划方案案例
  • 深圳龙华住房和建设局网站官网/百度教育app
  • 西安企业招聘官网/网店seo名词解释
  • 网站建设需要实现哪些目标/东莞谷歌推广公司
  • 广州 营销型网站建设/关键词检测工具