前言:
泛型类,泛型方法,泛型接口,通配符,类型擦除
文章目录
- [一、 泛型](#一、 泛型)
-
- 1.1、泛型的基本概念
- [1.2 泛型的使用](#1.2 泛型的使用)
- 三、通配符(Wildcard)
- [四、类型擦除(Type Erasure)](#四、类型擦除(Type Erasure))
- 五、泛型的局限性
一、 泛型
Java泛型(Java Generics)是一种允许类、接口和方法操作不同数据类型而无需指定具体数据类型的机制。Java引入泛型是为了提供更高的类型安全性、可读性、可维护性和代码复用性,这在Java 5及以后版本中引入。
1.1、泛型的基本概念
-
泛型类
泛型类是指在类声明时带有一个或多个类型参数的类。这些类型参数在类的使用过程中被具体化。定义泛型类时,使用尖括号
<>
括起来的类型参数。javapublic class Box<T> { private T content; public void set(T content) { this.content = content; } public T get() { return content; } }
在上述例子中,
T
是一个类型参数,可以在使用时被具体化为任何类型。 -
泛型接口
泛型接口和泛型类类似,只是定义在接口上。
javapublic interface Container<T> { void add(T item); T remove(); }
-
泛型方法
泛型方法是在方法声明中引入一个或多个类型参数的方法。这些类型参数使方法可以独立于任何特定的类型工作。
javapublic class Util { public static <T> boolean compare(T t1, T t2) { return t1.equals(t2); } }
在这个例子中,
<T>
是在方法声明中引入的类型参数,可以在方法的参数列表和返回类型中使用。
注意
,泛型方法和泛型类中的普通方法有以下区别:
-
类型参数的定义位置不同
- 泛型方法:类型参数是在方法声明中定义的,方法可以在其签名中使用这些类型参数。
- 类中的普通方法:如果类是泛型类,那么普通方法可以使用类的类型参数,但不能定义自己的类型参数。
-
泛型方法的灵活性
- 泛型方法:可以在非泛型类中定义,也可以在泛型类中定义。每个方法可以有自己独立的类型参数。
- 类中的普通方法:只能使用类级别的类型参数(如果类是泛型类)。
在泛型类中定义泛型方法的示例
在泛型类中也可以定义泛型方法,且方法可以有不同的类型参数:
java
public class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
// 泛型方法,使用独立的类型参数 U
public <U> void print(U item) {
System.out.println(item);
}
}
1.2 泛型的使用
-
泛型类的使用
在实例化泛型类时,需要为类型参数提供具体的类型。
javaBox<String> stringBox = new Box<>(); stringBox.set("Hello"); String content = stringBox.get(); System.out.println(content);
-
泛型方法的使用
调用泛型方法时,可以显式指定类型参数,也可以让编译器通过类型推断自动确定类型参数。
javaString s1 = "test"; String s2 = "test"; boolean result = Util.<String>compare(s1, s2); // 显式指定类型参数 boolean result2 = Util.compare(s1, s2); // 通过类型推断
-
泛型接口的使用
实现泛型接口时,可以指定接口的类型参数。
javapublic interface Container<T> { void add(T item); T remove(); } // 实现泛型接口时具体化类型参数为 String public class StringContainer implements Container<String> { private List<String> items = new ArrayList<>(); @Override public void add(String item) { items.add(item); } @Override public String remove() { return items.isEmpty() ? null : items.remove(0); } }
或者实现类保留类型参数
javapublic interface Container<T> { void add(T item); T remove(); } // 让实现类保留类型参数 public class GenericContainer<T> implements Container<T> { private List<T> items = new ArrayList<>(); @Override public void add(T item) { items.add(item); } @Override public T remove() { return items.isEmpty() ? null : items.remove(0); } }
三、通配符(Wildcard)
-
无界通配符
<?>
表示任何类型。这在需要表示不确定的类型时很有用。javapublic void printList(List<?> list) { for (Object obj : list) { System.out.println(obj); } }
-
上界通配符
<? extends T>
表示类型是T
或T
的子类。javapublic void processElements(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } }
-
下界通配符
<? super T>
表示类型是T
或T
的父类。javapublic void addNumbers(List<? super Integer> list) { list.add(1); list.add(2); list.add(3); }
四、类型擦除(Type Erasure)
在Java中,泛型类型在编译时会被擦除,也就是Java编译器将泛型代码转换为非泛型代码,这意味着在运行时,泛型类型信息将不可用。这是为了向后兼容Java 5之前的版本。在类型擦除过程中:
- 泛型类型参数被替换为其上界(若未指定上界,则为Object)。
- 插入类型转换,以保证类型安全。
java
public class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
// 编译后,大致变成以下形式
// private Object content;
// public void set(Object content) { this.content = content; }
// public Object get() { return content; }
}
五、泛型的局限性
尽管泛型提供了很多好处,但它也有一些限制:
-
不能实例化泛型类型:因为类型擦除的存在,无法在运行时获取类型参数的具体类型。
javapublic class Box<T> { // 错误:不能实例化类型参数 // private T content = new T(); }
-
不能创建泛型数组:数组在运行时会保留类型信息,而泛型在运行时会被擦除。
javapublic class Box<T> { // 错误:不能创建泛型数组 // T[] array = new T[10]; }
-
不能使用基本类型作为类型参数:泛型只能用于引用类型。
javaBox<int> intBox = new Box<>(); // 错误:不能使用基本类型作为类型参数 Box<Integer> integerBox = new Box<>(); // 正确
-
不能在静态上下文中使用泛型类型参数:因为静态成员属于类,而不属于某个特定的对象。
javapublic class Box<T> { // 错误:静态字段不能使用类型参数 // private static T content; }
如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。