在 Java 中的 Stream() 上的三元运算符中使用“instanceof”测试混合类型列表的每个元素时出现推理错误

问题描述 投票:0回答:1

我知道这可能是 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
java types stream conditional-operator instanceof
1个回答
0
投票

由于

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);
    
© www.soinside.com 2019 - 2024. All rights reserved.