具有泛型的 Vavr 给出了不兼容的类型
Vavr with generics gives incompatible types
任何人都可以解释为什么这个代码:
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.of(() -> List.of(1, 2, 3))
//.onFailure(Object::hashCode)
;
}
}
如果我取消注释 onFailure
语句会编译失败吗?不知道这里发生了什么。如何改进?
您可以调用 Try.of()
并返回显式泛型以满足编译器检查。类似于:
Try.<Seq<? extends Number>of(() -> List.of(1,2,3))
Try.of()
returns type Try<T>
其中 T
是供应商返回的类型。而因为List.of(T t...)
returnsList<T>
,那么编译器看到的最终类型是Try<List<Integer>
,这不是方法定义的返回类型。 Java 具有特定类型的泛型是不变的,它们不支持协变或逆变替换,所以 List<Integer> != List<Number>
.
工作示例:
import io.vavr.collection.List;
import io.vavr.collection.Seq;
import io.vavr.control.Try;
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.<Seq<? extends Number>>of(() -> List.of(1, 2, 3))
.onFailure(t -> System.out.println(t.getMessage()));
}
public static void main(String[] args) {
System.out.println(new LolImpl().lol());
}
}
输出:
Success(List(1, 2, 3))
通用示例类型推断问题
进一步调查表明这很可能是一般编译器问题。看看下面的简单 Java 示例:
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
interface Some<T> {
static <T> Some<T> of(Supplier<T> supplier) {
return new SomeImpl<>(supplier.get());
}
default Some<T> shout() {
System.out.println(this);
return this;
}
class SomeImpl<T> implements Some<T> {
private final T value;
public SomeImpl(T value) {
this.value = value;
}
}
static void main(String[] args) {
final Some<List<CharSequence>> strings = Some.of(() -> Arrays.asList("a", "b", "c"));
}
}
此代码编译没有任何问题,编译器从左侧的预期类型推断出 Arrays.asList()
返回的类型:
现在,如果我调用此 Some<T>.shout()
方法,它什么也不做,并且 returns Some<T>
,编译器不是从预期的变量类型推断类型,而是从最后返回的类型推断类型:
当然 Arrays.asList("a","b","c")
returns List<String>
和 this is the type
shout()` 方法推断 returns:
指定 Some<T>.of()
的显式类型解决了 Try.of()
示例中的问题:
我正在搜索关于类型推断的 Oracle 文档,有这样的解释:
The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears.
Source: https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types
看起来像这样 "depending on where the expression appears" 在这种情况下意味着从先前返回的确切类型推断类型。这将解释为什么跳过 shout()
方法让编译器知道,我们期望 Some<List<CharSequence>>
并且当我们添加 shout()
方法时它开始返回 Some<List<String>>
,因为这就是 shout()
]方法从Some.of()
方法的返回类型来看。希望对你有帮助。
似乎 Java 编译器无法为您推断出正确的类型,在这种情况下,您需要提供继续进行所需的其他类型信息,例如:
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
Try<Seq<? extends Number>> res = Try.of(() -> List.of(1, 2, 3));
return res.onFailure(Object::hashCode);
}
}
TL;DR
您问题的答案与 Java 的类型推断以及类型方差(在我们的例子中是协方差)有关。它与 Vavr 无关。
Try<List<Integer>>
是 Try<? extends Seq<? extends Number>>
的子类型。
- 但是
Try<List<Integer>>
不是 Try<Seq<? extends Number>>
的子类型。
将 lol()
方法的 return 类型更改为 Try<? extends Seq<? extends Number>>
,所有编译都可以正常进行。
让我们详细了解一下。
public Try<Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
//.onFailure(Object::hashCode) // line 3
;
}
lol()
方法执行 return 类型 Try<Seq<? extends Number>>
的值(参见第 1 行)。
第 2 行中的 return 语句 return 是使用工厂方法 Try.of(...)
构造的 Try
的一个实例。在Vavr 0.9.x中是这样定义的:
static <T> Try<T> of(CheckedFunction0<? extends T> supplier) {
// implementation omitted
}
编译器推断:
// type T = Seq<? extends Number>
Try.of(() -> List.of(1, 2, 3))
因为它需要同时匹配方法lol()
的return类型和工厂方法Try.of
的CheckedFunction0
签名。
这编译得很好,因为 supplier
函数 return 是 ? extends T
类型的值,即 ? extends Seq<? extends Number>
,与实际的 return 兼容] 输入 List<Integer>
(参见上面的 TL;DR 部分)。
如果我们现在取消注释 .onFailure
部分(第 3 行),那么工厂方法 Try.of
的泛型类型参数 T
不具有 [=88] 的范围=] 类型 lol()
了。编译器将 T
推断为 List<Integer>
,因为它总是试图找到适用的最具体的类型。
.onFailure
return 是类型 List<Integer>
的值,因为它 return 如果它的实例是完全相同的类型。但是 Try<List<Integer>>
不是 Try<Seq<? extends Number>>
的子类型(参见上面的 TL;DR 部分),因此代码不再编译。
在其 return 类型中使 lol()
方法 协变 将满足编译器的要求:
// before: Try<Seq<? extends Number>>
Try<? extends Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
.onFailure(Object::hashCode); // line 3
}
顺便说一句,在 Vavr 的整个类型层次结构中定义正确的泛型变体,尤其是对于集合,是创建 Vavr 时的困难部分之一。 Java的类型系统并不完美,还有一些东西我们无法用Java的泛型表达。另见我的博客 post "Declaration-Site Variance in a Future Java"
免责声明:我是 Vavr(以前称为 Java俚语)的创造者
任何人都可以解释为什么这个代码:
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.of(() -> List.of(1, 2, 3))
//.onFailure(Object::hashCode)
;
}
}
如果我取消注释 onFailure
语句会编译失败吗?不知道这里发生了什么。如何改进?
您可以调用 Try.of()
并返回显式泛型以满足编译器检查。类似于:
Try.<Seq<? extends Number>of(() -> List.of(1,2,3))
Try.of()
returns type Try<T>
其中 T
是供应商返回的类型。而因为List.of(T t...)
returnsList<T>
,那么编译器看到的最终类型是Try<List<Integer>
,这不是方法定义的返回类型。 Java 具有特定类型的泛型是不变的,它们不支持协变或逆变替换,所以 List<Integer> != List<Number>
.
工作示例:
import io.vavr.collection.List;
import io.vavr.collection.Seq;
import io.vavr.control.Try;
interface Lol {
default Try<Seq<? extends Number>> lol() {
return Try.of(List::empty);
}
}
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
return Try
.<Seq<? extends Number>>of(() -> List.of(1, 2, 3))
.onFailure(t -> System.out.println(t.getMessage()));
}
public static void main(String[] args) {
System.out.println(new LolImpl().lol());
}
}
输出:
Success(List(1, 2, 3))
通用示例类型推断问题
进一步调查表明这很可能是一般编译器问题。看看下面的简单 Java 示例:
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
interface Some<T> {
static <T> Some<T> of(Supplier<T> supplier) {
return new SomeImpl<>(supplier.get());
}
default Some<T> shout() {
System.out.println(this);
return this;
}
class SomeImpl<T> implements Some<T> {
private final T value;
public SomeImpl(T value) {
this.value = value;
}
}
static void main(String[] args) {
final Some<List<CharSequence>> strings = Some.of(() -> Arrays.asList("a", "b", "c"));
}
}
此代码编译没有任何问题,编译器从左侧的预期类型推断出 Arrays.asList()
返回的类型:
现在,如果我调用此 Some<T>.shout()
方法,它什么也不做,并且 returns Some<T>
,编译器不是从预期的变量类型推断类型,而是从最后返回的类型推断类型:
当然 Arrays.asList("a","b","c")
returns List<String>
和 this is the type
shout()` 方法推断 returns:
指定 Some<T>.of()
的显式类型解决了 Try.of()
示例中的问题:
我正在搜索关于类型推断的 Oracle 文档,有这样的解释:
The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears.
Source: https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types
看起来像这样 "depending on where the expression appears" 在这种情况下意味着从先前返回的确切类型推断类型。这将解释为什么跳过 shout()
方法让编译器知道,我们期望 Some<List<CharSequence>>
并且当我们添加 shout()
方法时它开始返回 Some<List<String>>
,因为这就是 shout()
]方法从Some.of()
方法的返回类型来看。希望对你有帮助。
似乎 Java 编译器无法为您推断出正确的类型,在这种情况下,您需要提供继续进行所需的其他类型信息,例如:
class LolImpl implements Lol {
@Override
public Try<Seq<? extends Number>> lol() {
Try<Seq<? extends Number>> res = Try.of(() -> List.of(1, 2, 3));
return res.onFailure(Object::hashCode);
}
}
TL;DR
您问题的答案与 Java 的类型推断以及类型方差(在我们的例子中是协方差)有关。它与 Vavr 无关。
Try<List<Integer>>
是Try<? extends Seq<? extends Number>>
的子类型。- 但是
Try<List<Integer>>
不是Try<Seq<? extends Number>>
的子类型。
将 lol()
方法的 return 类型更改为 Try<? extends Seq<? extends Number>>
,所有编译都可以正常进行。
让我们详细了解一下。
public Try<Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
//.onFailure(Object::hashCode) // line 3
;
}
lol()
方法执行 return 类型 Try<Seq<? extends Number>>
的值(参见第 1 行)。
第 2 行中的 return 语句 return 是使用工厂方法 Try.of(...)
构造的 Try
的一个实例。在Vavr 0.9.x中是这样定义的:
static <T> Try<T> of(CheckedFunction0<? extends T> supplier) {
// implementation omitted
}
编译器推断:
// type T = Seq<? extends Number>
Try.of(() -> List.of(1, 2, 3))
因为它需要同时匹配方法lol()
的return类型和工厂方法Try.of
的CheckedFunction0
签名。
这编译得很好,因为 supplier
函数 return 是 ? extends T
类型的值,即 ? extends Seq<? extends Number>
,与实际的 return 兼容] 输入 List<Integer>
(参见上面的 TL;DR 部分)。
如果我们现在取消注释 .onFailure
部分(第 3 行),那么工厂方法 Try.of
的泛型类型参数 T
不具有 [=88] 的范围=] 类型 lol()
了。编译器将 T
推断为 List<Integer>
,因为它总是试图找到适用的最具体的类型。
.onFailure
return 是类型 List<Integer>
的值,因为它 return 如果它的实例是完全相同的类型。但是 Try<List<Integer>>
不是 Try<Seq<? extends Number>>
的子类型(参见上面的 TL;DR 部分),因此代码不再编译。
在其 return 类型中使 lol()
方法 协变 将满足编译器的要求:
// before: Try<Seq<? extends Number>>
Try<? extends Seq<? extends Number>> lol() { // line 1
return Try.of(() -> List.of(1, 2, 3)) // line 2
.onFailure(Object::hashCode); // line 3
}
顺便说一句,在 Vavr 的整个类型层次结构中定义正确的泛型变体,尤其是对于集合,是创建 Vavr 时的困难部分之一。 Java的类型系统并不完美,还有一些东西我们无法用Java的泛型表达。另见我的博客 post "Declaration-Site Variance in a Future Java"
免责声明:我是 Vavr(以前称为 Java俚语)的创造者