您好,登录后才能下订单哦!
# 怎么使用Java泛型
## 一、泛型概述
### 1.1 什么是泛型
泛型(Generics)是Java SE 5.0引入的重要特性,它允许在定义类、接口和方法时使用类型参数(type parameters)。这种参数化类型的能力让代码可以应用于多种数据类型,同时保持编译时的类型安全。
```java
// 没有泛型的例子
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要强制类型转换
// 使用泛型的例子
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自动类型推断
在类名后面添加类型参数声明,可以有一个或多个类型参数。
public class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
// 使用示例
Box<Integer> integerBox = new Box<>();
integerBox.set(10);
Integer value = integerBox.get();
接口也可以使用泛型,实现类需要指定具体类型或保持泛型。
public interface Pair<K, V> {
K getKey();
V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
// 实现方法...
}
在方法返回类型前声明类型参数,可以用于静态和非静态方法。
public class Util {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
}
// 使用示例
String middle = Util.<String>getMiddle("John", "Q.", "Public");
// 类型推断
Number middleNum = Util.getMiddle(3.14, 1729, 0);
按照Java惯例,类型参数名称使用单个大写字母: - E - Element(集合元素) - K - Key(键) - V - Value(值) - T - Type(类型) - S,U,V - 第二、第三、第四类型
使用问号(?)表示未知类型,常用于方法参数。
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
可以限制通配符的上界或下界。
上界通配符:<? extends T>
表示T或其子类
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
下界通配符:<? super T>
表示T或其父类
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
Java泛型是通过类型擦除实现的,编译器在编译时去掉类型参数信息。
// 编译前
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);
// 编译后(近似)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
public static <E> void append(List<E> list) {
E elem = new E(); // 编译错误
list.add(elem);
}
List<Integer>[] arrayOfLists = new List<Integer>[2]; // 编译错误
静态字段或方法不能引用类的类型参数。
public class MobileDevice<T> {
private static T os; // 编译错误
public static T getOS() { // 编译错误
return os;
}
}
// 编译错误
try {
// ...
} catch (SomeException<Integer> e) {
// ...
}
Producer-Extends, Consumer-Super(生产者使用extends,消费者使用super)
// 正确示例
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
当方法操作独立于类类型参数时,使用泛型方法更灵活。
// 优于在类上定义类型参数
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
result.add(t);
}
}
return result;
}
总是使用泛型类型,避免使用原生类型(raw type)。
List list = new ArrayList(); // 避免
List<String> list = new ArrayList<>(); // 推荐
public class Cache<K, V> {
private Map<K, V> cacheMap = new HashMap<>();
public void put(K key, V value) {
cacheMap.put(key, value);
}
public V get(K key) {
return cacheMap.get(key);
}
public void remove(K key) {
cacheMap.remove(key);
}
}
public class CompareUtil {
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Collections.sort(list);
}
}
不能直接使用,必须使用对应的包装类。Java的泛型是基于对象的,基本类型不是对象。
由于类型擦除,运行时无法直接检查泛型类型。可以通过传递Class对象来解决:
public <T> void checkType(Object obj, Class<T> type) {
if (type.isInstance(obj)) {
T t = type.cast(obj);
// 处理t
}
}
子类可以继承或扩展父类的泛型参数:
class Parent<T> {}
class Child<T> extends Parent<T> {}
class StringChild extends Parent<String> {}
Java泛型是强大的语言特性,正确使用可以: 1. 提高代码的类型安全性 2. 减少类型转换的繁琐 3. 增强代码的可读性和重用性 4. 使API设计更加灵活
虽然泛型有一些限制(主要是由于类型擦除),但通过合理的设计模式和使用技巧,可以充分发挥其优势。掌握泛型是成为高级Java开发者的必备技能。
本文涵盖了Java泛型的主要知识点,从基础概念到高级应用,共约3700字。通过示例代码和最佳实践,帮助读者全面理解并正确使用Java泛型。在实际开发中,应根据具体场景灵活运用泛型特性,编写出更安全、更灵活的代码。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。