泛型的定义
定义1:泛型接口
一种具有类型参数的接口。这意味着当实现这个接口的时候,必须指定具体的类型参数。这样,该接口中的所有抽象方法都可以操作指定的数据类型。
public interface Container<T> {public abstract void add(T item);public abstract T get(int index);
}
T
是一个类型参数,代表某种未知的数据类型。任何实现Container
接口的类都需要提供T
的具体类型,比如String
、Integer
等。
在Java泛型中,类型参数通常是单个大写字母,它们代表的是未知的数据类型。这些字母的选择并不是强制性的,但有一些常见的惯例来帮助开发者快速理解代码的意图。
下面是几个常用的类型参数及其含义:
- T - Type(类型): 这是最常用的类型参数,通常用来表示“任意类型”。当没有更具体的含义时,可以使用
T
。 - E - Element(元素): 通常用于表示集合中的元素类型,如列表或数组中的元素。例如,在
java.util.List<E>
接口中,E
就代表列表中的元素类型。 - K - Key(键): 一般用于映射(Map)接口中,代表键的类型。例如,在
java.util.Map<K, V>
接口中,K
就是键的类型。 - V - Value(值): 同样常用于映射(Map)接口中,代表与键关联的值的类型。在
java.util.Map<K, V>
中,V
就是值的类型。 - N - Number(数字): 有时用来指代数值类型,比如
Integer
或Double
等。
接下来,创建一个实现了这个接口的具体类StringContainer
,专门用于存储字符串
public class StringContainer implements Container<String> {private List<String> items = new ArrayList<>();@Overridepublic void add(String item) {items.add(item);}@Overridepublic String get(int index) {return items.get(index);}
}
定义2:泛型类
包含一个或多个类型参数的类。创建该类的对象时,需要为这些类型参数提供具体的数据类型。这使得类能够以一种类型安全的方式处理不同类型的数据。
public class Box<T> {private T content;public void setContent(T content) {this.content = content;}public T getContent() {return content;}
}
Box
类可以用作存放任何类型对象的容器。例如:可以创建Box<String>
或者Box<Integer>
来分别存放字符串和整数。
public static void main(String[] args) {// 创建一个可以存放整数的盒子Box<Integer> intBox = new Box<>();intBox.setContent(42);System.out.println(intBox.getContent());// 创建一个可以存放字符串的盒子Box<String> stringBox = new Box<>();stringBox.setContent("Hello, world!");System.out.println(stringBox.getContent());
}
定义3:泛型方法
在方法级别上使用的泛型。它们可以在非泛型类中定义,并且可以有自己独立于类本身的类型参数。泛型方法的好处是可以在不修改整个类的情况下添加泛型功能。
public class Utils {public static <E> void printArray(E[] elements) {for (E element : elements) {System.out.println(element);}}
}
工具类Utils
中包含了一个泛型方法printArray
,该方法可以打印出数组中的所有元素。
public static void main(String[] args) {Integer[] intArray = {1, 2, 3, 4, 5};String[] strArray = {"a", "b", "c", "d"};// 打印整数数组Utils.printArray(intArray);// 打印字符串数组Utils.printArray(strArray);
}
泛型方法 printArray
能够处理不同类型数组的打印工作。
通配符(wildcards)
通配符提供了一种方式来指定类型参数的未知部分。通配符使得泛型更加灵活,同时保持了类型安全。Java中的通配符主要有三种形式:
通配符 | 符号表示 | 描述 | 应用场景 |
---|---|---|---|
无界通配符 | <?> | 表示可以是任何类型 | 通常用于当不关心具体的类型是什么,但需要保证类型安全性的情况 |
上界通配符 | <? extends T> | 表示类型必须是T 或T 的子类 | 适用于读取操作(如获取元素) |
下界通配符 | <? super T> | 表示类型必须是T 或T 的父类 | 适用于写入操作(如添加元素)限制:不允许添加新的元素,因为不确定具体的类型。 |
<?>
限制:不能向使用无界通配符声明的集合中添加非 null
元素,因为编译器不知道集合的确切类型。
List<?> list = new ArrayList<String>();
// list.add("test"); // 编译错误
list.add(null); // 可以添加 null
<? extends T>
限制:不允许添加新的元素,因为不确定具体的类型。
List<? extends Number> numbers = new ArrayList<Integer>(); // Integer 是 Number 的子类
Number num = numbers.get(0); // 可以获取元素
// numbers.add(1); // 编译错误,不能添加元素
<? super T>
限制:从这样的集合中取出的数据只能视为 Object
类型。
List<? super Integer> integers = new ArrayList<Number>(); // Number 是 Integer 的父类
integers.add(1); // 可以添加元素
// Integer i = integers.get(0); // 编译错误,只能获取 Object 类型
Object obj = integers.get(0); // 获取到的是 Object 类型