[英]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()
返回類型Try<T>
,其中T
是供應商返回的類型。 並且因為List.of(T t...)
返回List<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()
方法,它什么都不做並返回Some<T>
,編譯器推斷的類型不是來自預期的變量類型,而是來自最后返回的類型:
當然Arrays.asList("a","b","c")
返回List<String>
, this is the type
shout()`方法推斷並返回:
指定Some<T>.of()
Try.of()
顯式類型可以解決問題,如Try.of()
示例中所示:
我正在搜索關於類型推斷的Oracle文檔,並且有這樣的解釋:
Java編譯器利用目標類型來推斷泛型方法調用的類型參數。 表達式的目標類型是Java編譯器所期望的數據類型,具體取決於表達式的顯示位置。
資料來源: https : //docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types
看起來這“取決於表達式出現的位置”在這種情況下意味着來自先前返回的確切類型的推斷類型。 這將解釋為什么跳過shout()
方法使編譯器意識到,我們期望Some<List<CharSequence>>
並且當我們添加shout()
方法時它開始返回Some<List<String>>
,因為這就是shout()
方法從返回的Some.of()
方法類型中看到。 希望能幫助到你。
您的問題的答案與Java的類型推斷以及類型方差(在我們的案例中為協方差)有關。 它與Vavr無關。
Try<List<Integer>>
是Try<? extends Seq<? extends Number>>
的子類型 Try<? extends Seq<? extends Number>>
Try<? extends Seq<? extends Number>>
。 Try<List<Integer>>
不是Try<Seq<? extends Number>>
的子類型Try<Seq<? extends Number>>
Try<Seq<? extends Number>>
。 將lol()
方法的返回類型更改為Try<? extends Seq<? extends Number>>
Try<? extends Seq<? extends Number>>
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()
方法確實返回Try<Seq<? extends Number>>
類型的值Try<Seq<? extends Number>>
Try<Seq<? extends Number>>
(見第1行)。
第2行中的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()
的返回類型和工廠方法Try.of
的CheckedFunction0
簽名。
編譯很好,因為supplier
函數返回的類型值? extends T
? extends T
,這是? extends Seq<? extends Number>
? extends Seq<? extends Number>
? extends Seq<? extends Number>
,它與實際返回類型List<Integer>
兼容(參見上面的TL; DR部分)。
如果我們現在取消注釋.onFailure
部分(第3行),那么工廠方法Try.of
的泛型類型參數T
不再具有返回類型lol()
的范圍。 編譯器推斷T
為List<Integer>
因為它總是試圖找到適用的最具體的類型。
.onFailure
返回List<Integer>
類型的值,因為如果它的實例返回完全相同的類型。 但是Try<List<Integer>>
不是Try<Seq<? extends Number>>
的子類型Try<Seq<? extends Number>>
Try<Seq<? extends Number>>
(參見上面的TL; DR部分),因此代碼不再編譯。
使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的泛型無法表達的東西。 另請參閱我的博客文章“聲明 - 未來Java中的站點差異”
免責聲明:我是Vavr(前身為Javaslang)的創始人
看起來像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);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.