赞
踩
Java 泛型(generics)是 JDK 5 中引入的, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的好处是
在编译的时候检查类型安全;
避免了强制类型转换运行时异常;
同一个类可以操作多种类型数据,提高代码复用。
Java 中泛型标记符
在 Java 中,泛型标记符用于表示类型参数。下面是一些常用的泛型标记符及其含义:
这些泛型标记符只是一种约定,并没有强制规定必须使用它们,你可以使用任何可以作为标识符的字符来表示类型参数。但是,为了代码的可读性和可维护性,建议使用这些标记符。
为什么使用泛型?
如果要实现不同类型的加法,每种类型都需要重载一个add方法。使用泛型适用于多种数据类型执行相同的代码,示例源码如下:
- public class NeedGeneric1 {
-
- private static int add(int a, int b) {
- System.out.println(a + "+" + b + "=" + (a + b));
- return a + b;
- }
-
- private static float add(float a, float b) {
- System.out.println(a + "+" + b + "=" + (a + b));
- return a + b;
- }
-
- private static double add(double a, double b) {
- System.out.println(a + "+" + b + "=" + (a + b));
- return a + b;
- }
-
- //使用泛型
- private static <T extends Number> double add(T a, T b) {
- System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
- return a.doubleValue() + b.doubleValue();
- }
-
- public static void main(String[] args) {
- NeedGeneric1.add(1, 2); //1+2=3
- NeedGeneric1.add(1f, 2f); //1.0+2.0=3.0
- NeedGeneric1.add(1d, 2d); //1.0+2.0=3.0
-
- NeedGeneric1.add(Integer.valueOf(1), Integer.valueOf(2)); //1+2=3.0
- NeedGeneric1.add(Float.valueOf(1), Float.valueOf(2)); //1.0+2.0=3.0
- NeedGeneric1.add(Double.valueOf(1), Double.valueOf(2)); //1.0+2.0=3.0
- }
- }
下面举几个例子来说明Java泛型的应用。
1.泛型类
定义一个泛型类:public class GenericClass<T>{},例如:
- //泛型类例子
- public class GenericClass<T> {
- private T data;
-
- public T getData() {
- return data;
- }
-
- public void setData(T data) {
- this.data = data;
- }
-
- public static void main(String[] args) {
- GenericClass<String> genericClass=new GenericClass<>();
- genericClass.setData("Generic Class");
- System.out.println(genericClass.getData()); //Generic Class
- }
- }
2.泛型方法
定义一个泛型方法: private static<T> TgenericAdd(T a, T b) {},例如:
- //泛型方法例子
- public class GenericMethod1 {
- private static int add(int a, int b) {
- System.out.println(a + "+" + b + "=" + (a + b));
- return a + b;
- }
-
- private static <T> T genericAdd(T a, T b) {
- System.out.println(a + "+" + b + "="+a+b);
- return a;
- }
-
- public static void main(String[] args) {
- GenericMethod1.add(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) {
- this.data = data;
- }
-
- @Override
- public T getData() {
- return data;
- }
-
- public static void main(String[] args) {
- ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
- implGenericInterface1.setData("Generic Interface1");
- System.out.println(implGenericInterface1.getData()); //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();
- System.out.println(implGenericInterface2.getData()); //Generic Interface2
- }
- }
4.泛型通配符
Java中的泛型通配符是指 "?" 符号,表示一个未知类型的通配符。它可以用作方法参数、方法返回值和类中的成员变量等地方。
extends关键字设定上行边界,即指明参数类型的顶层类,限定实例化泛型类时传入的具体类型,只能是继承自顶层类的。
super关键字设置下行边界,即指定参数类型的底层类,限定传入的参数类型只能是设定类的父类。
通配符有以下三种使用方式:
<? extends T> 表示类型上界,表示类型必须是T或T的子类。
<? super T> 表示类型下界,表示类型必须是T或T的父类。
<?> 表示无限制通配符,表示可以是任意类型。
使用通配符可以使代码更加灵活,特别是在涉及到多态性和继承关系时。但需要注意,对于带有通配符的泛型类型,不能对其中的元素进行添加操作,只能进行读取操作。
下面给出 Java 泛型通配符示例
★以下是一个使用<? extends T>泛型通配符的示例代码
- //<? extends T>通配符示例
- import java.util.ArrayList;
- import java.util.List;
-
- public class ExampleA {
-
- public static void main(String[] args) {
- List<? extends Number> list = new ArrayList<>();
-
- List<Integer> intList = new ArrayList<>();
- intList.add(1);
- intList.add(2);
-
- List<Double> doubleList = new ArrayList<>();
- doubleList.add(1.0);
- doubleList.add(2.0);
-
- // 下面两行都会编译报错,
- // 因为在声明时使用了 <? extends Number> 上界并不能允许添加元素。
- // list.add(3);
- // list.add(3.0);
-
- // 可以读取列表的元素,读取操作是允许的。
- int sum = 0;
- for (Number n : intList) {
- //System.out.println(n);
- sum += n.intValue();
- }
- System.out.println("sum: " + sum);//sum: 3
- double sumB = 0;
- for (Number n : doubleList) {
- //System.out.println(n);
- sumB += n.doubleValue();
- }
- System.out.println("sum: " + sumB);//sum: 3.0
- }
- }
在这个例子中,我们声明了一个 List<? extends Number> 类型的列表 list,它表示一个包含 Number 类型或其子类类型的列表。尽管 list 是一个列表类型的变量,但是我们不能向其添加任何元素,因为 <? extends Number> 上界限制了可以添加到列表中的元素类型。我们可以安全地从 list 中读取元素,并且这些元素都是 Number 类型或其子类类型。
★以下是一个使用<? super T>泛型通配符的示例代码:
- //<? super T>通配符示例
- import java.util.ArrayList;
- import java.util.List;
-
- public class ExampleB {
- public static void main(String[] args) {
- List<Number> numbers = new ArrayList<>();
- numbers.add(1);
- numbers.add(2.0);
- List<? super Integer> integers = numbers;
- integers.add(3);
- System.out.println(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 java.util.*;
-
- public class ExampleC {
- public static void printList(List<?> list) {
- for (Object elem : list)
- System.out.print(elem + " ");
- System.out.println();
- }
-
- public static void main(String[] args) {
- List<Integer> intList = Arrays.asList(1, 2, 3);
- List<String> strList = Arrays.asList("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) {
- this.value = value;
- }
-
- public T getValue() {
- return value;
- }
-
- public static void printValue(Example <?> example) {
- System.out.println(example.getValue());
- }
-
- public static void main(String[] args) {
- Example<Integer> intExample = new Example<Integer>();
- intExample.setValue(10);
- printValue(intExample); //10
-
- Example<String> stringExample = new Example<String>();
- stringExample.setValue("Hello, world!");
- printValue(stringExample); //Hello, world!
- }
- }
在上面的示例中,printValue 方法的参数类型为 Example <?>,这表示可以接受任何类型的 Example 实例,但是我们无法知道它的具体类型。这个通配符 ? 限制了我们对其进行操作的能力,但是仍然可以打印出值。
Java泛型类型可以继承或实现其他泛型类型或非泛型类型,但遵循以下规则:
泛型类型不能直接继承非泛型类型。
子类中的泛型类型可以比父类中的泛型类型具体化(即指定更具体的类型),但不能泛化(即使用更广泛的类型)。
当使用泛型类作为参数时,如果子类中的泛型类型比父类中的泛型类型具体化,则可以传递子类对象作为参数;反之则不行。
使用通配符(?)可以传递任意类型的泛型对象作为参数,但无法在方法中使用该泛型类型(因为我们无法确定其具体类型)。
总之,泛型类型之间的继承关系必须满足类型匹配的规则。
Java 泛型详解https://www.cnblogs.com/coprince/p/8603492.html
Java 中的泛型 https://blog.csdn.net/weixin_45395059/article/details/126006369
官方介绍
Lesson: Generics https://docs.oracle.com/javase/tutorial/extra/generics/
Generics (Updated) https://docs.oracle.com/javase/tutorial/java/generics/index.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。