Java中的泛型介绍
概述
Java 泛型(generics)是 JDK 5 中引入的, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的好处是
在编译的时候检查类型安全;
避免了强制类型转换运行时异常;
同一个类可以操作多种类型数据,提高代码复用。
Java 中泛型标记符
在 Java 中,泛型标记符用于表示类型参数。下面是一些常用的泛型标记符及其含义:
- E:代表元素(Element)类型,通常在集合中使用,如 List<E>。
- K:代表键(Key)的类型,通常在 Map 中使用,如 Map<K, V>。
- V:代表值(Value)的类型,通常在 Map 中使用,如 Map<K, V>。
- T:代表任意类型(Type),通常在方法中使用,如 public <T> T method(T obj)。
- N:代表数字类型,如 Number 类型。
- R:代表返回类型,如 public <T> R method(T obj)。
这些泛型标记符只是一种约定,并没有强制规定必须使用它们,你可以使用任何可以作为标识符的字符来表示类型参数。但是,为了代码的可读性和可维护性,建议使用这些标记符。
为什么使用泛型?
如果要实现不同类型的加法,每种类型都需要重载一个add方法。使用泛型适用于多种数据类型执行相同的代码,示例源码如下:
public class NeedGeneric1 {
private static int add(int a, int b) {
(a + "+" + b + "=" + (a + b));
return a + b;
}
private static float add(float a, float b) {
(a + "+" + b + "=" + (a + b));
return a + b;
}
private static double add(double a, double b) {
(a + "+" + b + "=" + (a + b));
return a + b;
}
//使用泛型
private static <T extends Number> double add(T a, T b) {
(a + "+" + b + "=" + (() + ()));
return () + ();
}
public static void main(String[] args) {
(1, 2); //1+2=3
(1f, 2f); //1.0+2.0=3.0
(1d, 2d); //1.0+2.0=3.0
((1), (2)); //1+2=3.0
((1), (2)); //1.0+2.0=3.0
((1), (2)); //1.0+2.0=3.0
}
}
Java泛型的应用
下面举几个例子来说明Java泛型的应用。
1.泛型类
定义一个泛型类:public class GenericClass<T>{},例如:
//泛型类例子
public class GenericClass<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
= data;
}
public static void main(String[] args) {
GenericClass<String> genericClass=new GenericClass<>();
("Generic Class");
(()); //Generic Class
}
}
2.泛型方法
定义一个泛型方法: private static<T> TgenericAdd(T a, T b) {},例如:
//泛型方法例子
public class GenericMethod1 {
private static int add(int a, int b) {
(a + "+" + b + "=" + (a + b));
return a + b;
}
private static <T> T genericAdd(T a, T b) {
(a + "+" + b + "="+a+b);
return a;
}
public static void main(String[] args) {
(1, 2); //1+2=3
GenericMethod1.<String>genericAdd("a", "b"); //a+b=ab
}
}
3.泛型接口
定义一个泛型接口文件:public interface GenericIntercace<T>{},例如:
//定义一个泛型接口文件
public interface GenericIntercace<T> {
T getData();
}
泛型接口分两种实现方法:
一是实现类不明确泛型接口的类型参数变量,这时实现类也必须定义类型参数变量;
二是明确泛型接口的类型参数变量。
实现泛型接口方式一,不明确泛型接口的类型参数变量:public class ImplGenericInterface1<T> implements GenericIntercace<T>,例如:
//泛型接口方式一,不指定具体类型实现方式
public class ImplGenericInterface1<T> implements GenericIntercace<T> {
private T data;
private void setData(T data) {
= data;
}
@Override
public T getData() {
return data;
}
public static void main(String[] args) {
ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
("Generic Interface1");
(()); //Generic Interface1
}
}
实现泛型接口方式二,明确泛型接口的类型参数变量:public class ImplGenericInterface2 implements GenericIntercace<String> {},例如:
//泛型接口方式二,指定具体类型实现方式
public class ImplGenericInterface2 implements GenericIntercace<String> {
@Override
public String getData() {
return "Generic Interface2";
}
public static void main(String[] args) {
ImplGenericInterface2 implGenericInterface2 = new ImplGenericInterface2();
(()); //Generic Interface2
}
}
4.泛型通配符
Java中的泛型通配符是指 "?" 符号,表示一个未知类型的通配符。它可以用作方法参数、方法返回值和类中的成员变量等地方。
extends关键字设定上行边界,即指明参数类型的顶层类,限定实例化泛型类时传入的具体类型,只能是继承自顶层类的。
super关键字设置下行边界,即指定参数类型的底层类,限定传入的参数类型只能是设定类的父类。
通配符有以下三种使用方式:
<? extends T> 表示类型上界,表示类型必须是T或T的子类。
<? super T> 表示类型下界,表示类型必须是T或T的父类。
<?> 表示无限制通配符,表示可以是任意类型。
使用通配符可以使代码更加灵活,特别是在涉及到多态性和继承关系时。但需要注意,对于带有通配符的泛型类型,不能对其中的元素进行添加操作,只能进行读取操作。
下面给出 Java 泛型通配符示例
★以下是一个使用<? extends T>泛型通配符的示例代码
//<? extends T>通配符示例
import ;
import ;
public class ExampleA {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
(1);
(2);
List<Double> doubleList = new ArrayList<>();
(1.0);
(2.0);
// 下面两行都会编译报错,
// 因为在声明时使用了 <? extends Number> 上界并不能允许添加元素。
// (3);
// (3.0);
// 可以读取列表的元素,读取操作是允许的。
int sum = 0;
for (Number n : intList) {
//(n);
sum += ();
}
("sum: " + sum);//sum: 3
double sumB = 0;
for (Number n : doubleList) {
//(n);
sumB += ();
}
("sum: " + sumB);//sum: 3.0
}
}
在这个例子中,我们声明了一个 List<? extends Number> 类型的列表 list,它表示一个包含 Number 类型或其子类类型的列表。尽管 list 是一个列表类型的变量,但是我们不能向其添加任何元素,因为 <? extends Number> 上界限制了可以添加到列表中的元素类型。我们可以安全地从 list 中读取元素,并且这些元素都是 Number 类型或其子类类型。
★以下是一个使用<? super T>泛型通配符的示例代码:
//<? super T>通配符示例
import ;
import ;
public class ExampleB {
public static void main(String[] args) {
List<Number> numbers = new ArrayList<>();
(1);
(2.0);
List<? super Integer> integers = numbers;
(3);
(integers); // 输出 [1, 2.0, 3]
}
}
这个示例中,我们首先创建了一个List<Number>对象,并向其中添加了两个元素:整数1和浮点数2.0。然后,我们将这个列表赋值给一个通配符类型的变量integers,该变量声明为<? super Integer>,表示它可以接受所有Integer的超类型(包括Integer本身)。最后,我们向integers列表中添加了一个整数3,这是完全合法的,因为Integer是Number的子类型。输出结果为[1, 2.0, 3]。
★以下是一个使用<? >泛型通配符的示例
例1、代码:
//<?>通配符示例
import .*;
public class ExampleC {
public static void printList(List<?> list) {
for (Object elem : list)
(elem + " ");
();
}
public static void main(String[] args) {
List<Integer> intList = (1, 2, 3);
List<String> strList = ("one", "two", "three");
printList(intList); //1 2 3
printList(strList); //one two three
}
}
在此示例中,我们定义了一个名为“printList”的静态方法,它接受一个未知类型的List作为参数,并打印每个元素。然后,我们创建一个Integer类型的List和一个String类型的List,并将它们分别传递给printList方法。由于该方法的参数类型是通配符,它可以接受任何类型的List,因此这两个列表都可以成功打印其元素。
例2、再给出一个<?>泛型通配符示例代码:
//又一个<?>泛型通配符示例
public class Example<T> {
private T value;
public void setValue(T value) {
= value;
}
public T getValue() {
return value;
}
public static void printValue(Example <?> example) {
(());
}
public static void main(String[] args) {
Example<Integer> intExample = new Example<Integer>();
(10);
printValue(intExample); //10
Example<String> stringExample = new Example<String>();
("Hello, world!");
printValue(stringExample); //Hello, world!
}
}
在上面的示例中,printValue 方法的参数类型为 Example <?>,这表示可以接受任何类型的 Example 实例,但是我们无法知道它的具体类型。这个通配符 ? 限制了我们对其进行操作的能力,但是仍然可以打印出值。
Java泛型类型可以继承或实现其他泛型类型或非泛型类型,但遵循以下规则:
泛型类型不能直接继承非泛型类型。
子类中的泛型类型可以比父类中的泛型类型具体化(即指定更具体的类型),但不能泛化(即使用更广泛的类型)。
当使用泛型类作为参数时,如果子类中的泛型类型比父类中的泛型类型具体化,则可以传递子类对象作为参数;反之则不行。
使用通配符(?)可以传递任意类型的泛型对象作为参数,但无法在方法中使用该泛型类型(因为我们无法确定其具体类型)。
总之,泛型类型之间的继承关系必须满足类型匹配的规则。
附录
Java 泛型详解/coprince/p/
Java 中的泛型 /weixin_45395059/article/details/126006369
官方介绍
Lesson: Generics /javase/tutorial/extra/generics/
Generics (Updated) /javase/tutorial/java/generics/