我知道这可能是 Java 中的设计缺陷,但我想处理具有未知类型的列表(在 Python 中很容易做到),用 lambda 函数映射每个元素,该函数测试类型并相应地输出一些内容。
我尝试处理整数列表和字符串,如下所示:
import java.util.Arrays;
import java.util.List;
public class TestMixedTypesArrays {
public static void main(String[] args) {
List<Object> myArray = Arrays.asList(2, 3, "a", 5);
myArray.stream().map(x -> x instanceof Integer ? x * x : x + x).forEach(System.out::println);
}
}
我希望能够根据每个元素的类型来处理它,但是编译器给出了以下异常:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>)
The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
The operator + is undefined for the argument type(s) java.lang.Object, java.lang.Object
由于
myArray
是 List<Object>
,因此 x
lambda 中 map
的类型是 Object
。也就是说.map(x -> [...])
相当于.map((Object x) -> [...])
。
Java 是一种静态类型语言,根据编译时已知的类型信息进行编译。
*
和 +
都不能应用于 Object
,因此当 x * x
类型为 x + x
时,x
和 Object
都无法编译。
为了在任意
*
上使用 +
或 Object
,必须首先将其转换为具有该操作的预期类型。这将导致 Java 代码在运行时验证该对象是否属于给定类型(如果不是,则抛出 ClassCastException
),然后允许将其作为强制转换类型进行操作。
实现此目的的经典方法是使用
instanceof
检查,然后强制转换值:
myArray.stream().map(x -> {
if (x instanceof Integer) {
Integer i = (Integer) x;
return i * i;
} else if (x instanceof String) {
String s = (String) x;
return s + s;
} else {
throw new IllegalArgumentException("Expected Integer or String");
}
}).forEach(System.out::println);
Java 16 通过 “instanceof 的模式匹配” 功能简化了此代码:
myArray.stream().map(x -> {
if (x instanceof Integer i) {
return i * i;
} else if (x instanceof String s) {
return s + s;
} else {
throw new IllegalArgumentException("Expected Integer or String");
}
}).forEach(System.out::println);
Java 21 通过“switch 表达式和语句的模式匹配” 功能进一步简化了这一过程:
myArray.stream().map(x -> switch (x) {
case Integer i -> i * i;
case String s -> s + s;
case null, default ->
throw new IllegalArgumentException("Expected Integer or String");
}).forEach(System.out::println);