Java泛型:如果你现在不掌握,以后会非常后悔!
什么是Java泛型?
Java泛型是Java SE 5引入的一种编程特性,它可以使我们编写能够处理不同数据类型的通用代码。通过使用泛型,我们可以更好地重用代码,并减少因类型错误而导致的运行时异常和安全问题。
泛型类
泛型类是一种可以处理多种数据类型的类。我们可以使用类型参数来定义泛型类。
概念
泛型类使用一个或多个类型变量作为其属性、方法参数或方法返回值的类型。在定义泛型类时,我们需要使用尖括号 < > 来指定类型变量。例如:
public class MyClass<T> { // 类定义}
在上面的代码中,<T> 是类型参数,表示这个类可以处理任意类型的数据。我们可以使用这个类来处理不同类型的数据。
用法
我们可以通过指定类型参数来创建一个具体的泛型类的实例对象。例如:
MyClass<Integer> myIntObj = new MyClass<>();myIntObj.setData(123);int myIntData = myIntObj.getData();
在上面的代码中,myIntObj 是一个 MyClass 类的实例对象,它具有处理整数类型的功能。我们可以通过调用 setData 方法设置对象中的数据,通过调用 getData 方法获取数据。
Java泛型类的使用场景
Java泛型类广泛应用于集合框架中,例如 List、Set、Map 等。这些集合类都是泛型类,可以处理任意类型的元素。下面是一个使用泛型类 List 的示例:
List<String> list = new ArrayList<>();list.add(&34;);list.add(&34;);for (String str : list) { System.out.println(str);}
在上面的示例中,List<String> 表示这个列表只能存储字符串类型的元素。我们可以使用 add() 方法向列表中添加元素,使用 for-each 循环遍历列表中的所有元素并打印。
泛型方法
泛型方法是一种可以处理多种数据类型的方法。我们可以使用类型参数来定义泛型方法。
概念
泛型方法使用一个或多个类型变量作为其参数或返回值的类型。在定义泛型方法时,我们需要使用尖括号 < > 来指定类型变量。例如:
public <T> void printArray(T[] array) { // 方法体}
在上面的代码中,<T> 是类型参数,可以表示多个类型参数。我们可以通过指定类型参数来调用泛型方法,例如:
Integer[] intArray = {1, 2, 3};String[] stringArray = {&34;, &34;};printArray(intArray); // 输出:1 2 3 printArray(stringArray); // 输出:Hello World
用法
泛型方法可以定义在泛型类中,也可以定义在非泛型类中。我们可以使用相同的类型参数来定义泛型类和泛型方法。
public class MyClass<T> { public <E> void printData(E data) { T t = null; // 方法体 }}
在上面的代码中,<E> 是定义在 MyClass 泛型类中的泛型方法。同时,T 是泛型类的类型参数。在泛型方法中,我们可以使用 T 来处理泛型类中的数据,使用 E 来处理泛型方法中的数据。
Java泛型方法的使用场景
Java泛型方法广泛应用于集合框架中,例如 Collections.sort()方法,可以为集合元素排序。下面是一个使用泛型方法 Collections.sort() 的示例:
List<String> list = new ArrayList<>();list.add(&34;);list.add(&34;);Collections.sort(list);for (String str : list) { System.out.println(str);}
在上面的示例中,Collections.sort() 是一个泛型方法,它可以对任意类型的列表进行排序。我们可以使用这个方法对 list 列表进行排序,然后使用 for-each 循环遍历列表中的所有元素并打印。
通配符
通配符是一种可以接受多种数据类型的类型参数。通配符可以使用 ? 代替具体的数据类型。
概念
通配符是一种特殊的类型参数,用于指定泛型类型的范围。Java中的通配符有两种形式:? extends T 和 ? super T。其中,? extends T 表示接受所有继承自 T 类的类型,包括 T 类本身;? super T 表示接受所有 T 类及其超类的类型。
例如,我们可以使用通配符来定义一个可以接受任何类型参数的方法:
public void printData(List<?> list) { for (Object obj : list) { System.out.print(obj + &34;); } System.out.println();}
在上面的代码中,List<?> 表示可以接受任何类型的列表。我们可以使用这个方法来打印任意类型的列表元素。
用法
通配符可以在泛型类和泛型方法中使用。我们可以使用通配符来定义参数、属性或方法返回值的类型。
public class MyClass<T> { private T data; public void setData(List<? extends T> list) { // 方法体 } public List<? super T> getData() { // 方法体 }}
在上面的代码中,<T> 是泛型类的类型参数,List<? extends T> 表示接受所有 T 类及其子类的类型列表,List<? super T> 表示接受所有 T 类及其超类的类型列表。
Java通配符的使用场景
Java通配符广泛应用于集合框架中,例如 List、Set、Map 等。下面是一个使用通配符 ? extends Number 的示例:
public double sum(List<? extends Number> list) { double sum = 0.0; for (Number number : list) { sum += number.doubleValue(); } return sum;}
在上面的示例中,List<? extends Number> 表示可以接受任何继承自 Number 类的数据类型。这样就可以处理 Integer、Double、Float 等不同类型的数据。
Java泛型的优点与局限性
优点
Java泛型的主要优点有:
提高代码可读性:泛型可以使代码更加清晰、易懂,从而提高代码的可读性和可维护性。提高代码复用性:泛型可以使代码更加通用、灵活,从而提高代码的复用性和可扩展性。减少类型转换错误:泛型可以在编译时发现并防止类型转换错误,从而减少因类型错误而导致的运行时异常和安全问题。局限性
Java泛型的主要局限性有:
运行时类型擦除:泛型信息只在编译时存在,而在运行时被擦除了。这意味着我们无法在运行时获取泛型类型的信息,从而限制了对泛型的使用。不能创建泛型数组:由于类型擦除机制的存在,Java 不允许创建泛型类型的数组。例如,List<String>[] array = new List<String>[10]; 这样的代码在 Java 中是非法的。不能实例化类型参数:我们不能直接通过 new T() 等方式来实例化泛型类型参数,因为在运行时它们的具体类型是未知的。现实中的使用场景
Java 泛型被广泛应用于许多方面,特别是在集合框架、文件 I/O、网络编程和数据库操作等领域。以下是一些示例:
集合框架
Java 集合框架通过泛型来提高类型安全性和代码重用性。例如,我们可以使用 List<String> 定义一个只包含字符串类型元素的列表,并使用通配符 <?> 来处理任意类型的列表。
List<String> list = new ArrayList<>();list.add(&34;);list.add(&34;);// 使用 for-each 循环遍历并打印列表中的所有元素for (String str : list) { System.out.println(str);}
文件 I/O
Java 文件 I/O 可以使用泛型来提高代码复用性和可维护性。例如,我们可以使用泛型类 BufferedReader<T> 来读取不同类型的输入流。
// 从标准输入读取字符串BufferedReader<String> reader = new BufferedReader<>(new InputStreamReader(System.in));String input = reader.readLine();// 从文件读取整数BufferedReader<Integer> reader = new BufferedReader<>(new FileReader(&34;));int input = reader.read();
网络编程
Java 网络编程可以使用泛型来处理不同类型的网络数据。例如,我们可以使用泛型类 DatagramPacket<T> 来发送和接收不同类型的数据包。
// 发送字符串数据包String data = &34;;byte[] buffer = data.getBytes();DatagramPacket<String> packet = new DatagramPacket<>(buffer, buffer.length, InetAddress.getLocalHost(), 1234);socket.send(packet);// 接收整数数据包byte[] buffer = new byte[1024];DatagramPacket<Integer> packet = new DatagramPacket<>(buffer, buffer.length);socket.receive(packet);int data = packet.getData();
数据库操作
Java 数据库操作可以使用泛型来处理不同类型的数据库表。例如,我们可以使用泛型类 ResultSet<T> 来查询不同类型的数据表。
// 查询字符串类型的数据表ResultSet<String> rs = statement.executeQuery(&39;%foo%&34;);while (rs.next()) { String data = rs.getString(&34;); System.out.println(data);}// 查询整数类型的数据表ResultSet<Integer> rs =statement.executeQuery(&34;);while (rs.next()) { int data = rs.getInt(&34;); System.out.println(data);}
总结
Java 泛型是一种强大的编程工具,可以提高代码的可读性、可维护性和复用性,并减少类型转换错误的风险。虽然 Java 泛型存在一些局限性,但它在现实中的应用广泛,特别是在集合框架、文件 I/O、网络编程和数据库操作等领域。因此,学习和掌握 Java 泛型是 Java 程序员必备的技能之一。