具有原始类型返回类型推断的 Java 嵌套泛型

问题描述 投票:0回答:1
public class Main {
    List<List<Integer>> f0() {
        return List.of(List.of(1L));
    }
    List<List<Integer>> f1() {
        return List.of((List) List.of(1L));
    }
    List<List<Integer>> f2() {
        var r = List.of((List) List.of(1L));
        return r;
    }
    List<List<Integer>> f3() {
        return List.of((List) List.of(1L), List.of(1L));
    }
    List<List<Integer>> f4() {
        return List.of((List) List.of(1L), (List) List.of(1L));
    }
}

在上面的代码中,

f1
f4
可以编译,而
f0
f2
f3
则不能编译。
我明白为什么
f0
无法编译:它需要
List<List<Integer>>
但它看到
List<List<Long>>
。但我不知道为什么其他人会这样做。

f2:

incompatible types: List<List> cannot be converted to List<List<Integer>>

f3:

error: incompatible types: inference variable E has incompatible bounds
                return List.of((List) List.of(1L), List.of(1L));
                              ^
    equality constraints: Integer
    lower bounds: Long
  where E is a type-variable:
    E extends Object declared in method <E>of(E)

在每个函数中,返回的表达式的类型到底是什么?为什么Java会有这样的行为?根据 JLS 的说法,到底发生了什么?如果重要的话我会使用 Java 17。

java generics type-inference return-type nested-generics
1个回答
0
投票

f2
f0
之间的区别在于
List.of
不在
f2
中的赋值上下文中,§14.4.1:

如果 LocalVariableType 为

var
,则将
T
视为初始化表达式的类型(当将其视为未出现在赋值上下文中时...

因此,调用不是聚表达式。它不满足 §15.12:

中作为多元表达式的要求

如果满足以下所有条件,则方法调用表达式是多元表达式:

  • 调用出现在赋值上下文或调用上下文中
  • ...

这意味着类型推断不再考虑目标类型 (§15.12.2.6)。

如果所选方法是泛型并且方法调用不提供显式类型参数,则按照第 18.5.2 节中的指定推断调用类型。

在这种情况下,如果方法调用表达式是多表达式,则其与目标类型的兼容性由§18.5.2.1确定。

跳过了整个 §18.5.2.1,它会添加一些关于

Integer
的约束。

但是,如果将

List.of
放在 return 语句中,则它 is 位于赋值上下文中 (§14.17):

如果带有值 Expression 的返回语句的返回目标是声明返回类型

T
的方法,并且 Expression 的类型与 T 不可分配兼容(第 5.2 节),则这是一个编译时错误。

(第 5.2 节是赋值上下文部分)

更通俗地说,Java 不会查看所有使用

r
来推断
List.of
的类型参数的地方。它只查看调用
List.of((List) List.of(1L))
。它被分配给
var
,因此类型推断结果为
List<List>
。类型推断没有理由在其中随机添加
<Integer>

当然,

List<List>
List<List<Integer>>
不兼容,就像
List<Dog>
List<Animal>
不兼容一样。


对于

f3
,存在相互冲突的约束。我将使用
E
来引用外部
List.of
调用的类型参数,并使用
E1
E2
来引用第一个和第二个内部
List.of
调用的类型参数。

因为外部

List.of
位于 return 语句中,所以适用 §18.5.2.1,并且
E
的下限为
List<Integer>

推断

E1
的下限为
Long
,则适用 §18.5.2.1,并且目标类型是原始
List
,即您要转换到的类型。没有什么问题,我们继续。

第一个参数中的原始

List
给出了
List
的上限
E
。没关系 - 这里没有冲突。

f4
中,第二个参数和
E2
也会发生同样的事情,所以没有问题。

f3
中,当推断
E2
时,适用§18.5.2.1,由于
List<Integer>
的约束,目标类型为
E
。这给
E2
添加了一个等式约束。参数
1L
还为
Long
添加了
E2
的下限。

这就是冲突所在。不存在同时满足

== Integer
>= Long
的类型。

© www.soinside.com 2019 - 2024. All rights reserved.