泛型相關
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
。