跳到主內容

泛型相關

Java 泛型是一种支持参数化类型的机制,它可以让代码更加通用和安全。通过使用泛型,我们可以在编写代码时使用占位符类型(如 T、E 等),然后在实际使用时再指定具体类型。这样就可以使代码更加灵活,可以避免类型转换错误和运行时异常。

Java 泛型的基本用法是使用尖括号 "<>" 括起来的一个或多个类型参数,这些类型参数可以用于类、接口、方法的声明中。例如:

public class MyClass<T> {
    private T data;

    public MyClass(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

在上面的代码中,我们使用了一个类型参数 T,这个参数可以用于 MyClass 类中的任何地方,包括类的字段、方法参数、方法返回值等等。

而<?>和<T>的区别在于,<?>是一种无限制通配符类型,表示可以匹配任何类型,而<T>则是一个类型参数,表示在使用时需要指定具体的类型。例如:

List<?> list = new ArrayList<>();
List<String> strList = new ArrayList<>();
list = strList; // 合法,因为 list 可以匹配任何类型
public <T> T getValue(T[] array, int index) {
    return array[index];
}

String[] strArray = {"hello", "world"};
String str = getValue(strArray, 0); // 合法,因为在使用 getValue 时指定了类型参数为 String

下面是一个使用无限制通配符类型(<?>)的例子,我们定义了一个方法,可以接受任何类型的 List,并打印出其中的元素:

public static void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}

这个方法的参数列表中使用了无限制通配符类型(<?>),表示可以匹配任何类型的 List。在方法内部,我们使用了一个 for-each 循环遍历 list,并打印出其中的元素。

使用这个方法时,我们可以传入任何类型的 List,例如:

List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> strList = Arrays.asList("hello", "world");
List<Object> objList = Arrays.asList(1, "hello", true);

printList(intList); // 输出 1, 2, 3
printList(strList); // 输出 hello, world
printList(objList); // 输出 1, hello, true

如果我们将上面的例子中的方法参数从无限制通配符类型(<?>)改为类型参数(<T>),那么代码将变成下面这样:

public static <T> void printList(List<T> list) {
    for (T element : list) {
        System.out.println(element);
    }
}

这个方法的参数列表中使用了类型参数(<T>),表示在使用时需要指定 List 中元素的具体类型。在方法内部,我们使用了一个 for-each 循环遍历 list,并打印出其中的元素。

使用这个方法时,我们需要指定 List 中元素的具体类型,例如:

List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> strList = Arrays.asList("hello", "world");
List<Object> objList = Arrays.asList(1, "hello", true);

printList(intList); // 输出 1, 2, 3
printList(strList); // 输出 hello, world
printList(objList); // 编译错误,因为 objList 中的元素类型不是 Object

在使用时,我们需要指定 List 中元素的具体类型,例如使用 printList(Integer)printList(String),这样才能保证参数类型的匹配,否则将会编译错误。


在 Java 泛型中,类型参数的名称是可以任意指定的,通常使用以下的约定:

  • T:表示任何类型。
  • E:表示集合中的元素类型。
  • K:表示映射中的键类型。
  • V:表示映射中的值类型。
  • N:表示数字类型。
  • S、U、V 等:表示其他任意类型。

因此,使用 <T> 或 <E> 在语义上是等价的,只是名称不同而已。

除了以上提到的常用类型参数名称外,开发者也可以根据实际情况自行定义类型参数的名称,只需要遵循命名规范即可。不过,为了代码可读性和可维护性,建议使用常见的类型参数名称。

需要注意的是,不同的类型参数名称并没有实质上的差异,只是在语义上有所不同,因此选择类型参数名称时应根据实际情况和需求进行选择,选择一个能更好地表达自己的代码意图的名称。


  一個回傳任意型別的範例

public static <T> T anyTypeMethod(T arg) {
    if (arg == null) {
        // 返回空对象
        return (T) new Object();
    }
    // 方法逻辑
    return arg;
}

在这个方法中,我们首先检查参数是否为 null。如果参数为 null,则返回一个空对象,否则执行方法的逻辑,将参数原封不动地返回。

需要注意的是,在这个方法中,我们创建了一个新的 Object 对象并将其转换为泛型类型,这是为了避免空对象时返回类型不匹配的问题。因为 Java 中的泛型是在编译时进行类型擦除的,如果我们直接返回一个 null,编译器将无法确定返回值的类型,可能会导致编译错误或运行时异常。

使用这个方法时,我们可以传入任何类型的参数,并获取返回值,如果传入参数为 null,则返回一个空对象,例如:

Integer intValue = anyTypeMethod(123);
String strValue = anyTypeMethod("hello");
Object objValue = anyTypeMethod(new Object());
Object nullValue = anyTypeMethod(null);

System.out.println(intValue); // 输出 123
System.out.println(strValue); // 输出 "hello"
System.out.println(objValue); // 输出 Object 对象的 toString() 值
System.out.println(nullValue); // 输出 "java.lang.Object@hashcode"

在这个示例中,我们分别传入了一个整数、一个字符串、一个对象和一个 null,然后获取了相应的返回值。当传入参数为 null 时,返回一个空对象,其类型为 Object