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