簡體   English   中英

Java自類型遞歸類型參數和javac中的繼承錯誤

[英]Java self type recursive type parameters and inheritance error in javac

為什么此代碼無法編譯?

public class x
{
    private void test()
    {
        handle(new ThingA());
        handle(new ModifiedThingA());
    }

    private <T extends BaseThing<T>, X extends T> java.util.List<T> handle(X object)
    {
        return object.getList();
    }

    private static class BaseThing<T extends BaseThing<T>>
    {
        public java.util.List<T> getList()
        {
            return null;
        }
    }

    private static class ThingA
        extends BaseThing<ThingA>
    {
    }

    private static class ModifiedThingA
        extends ThingA
    {
    }
}

Java 6在handle(new ModifiedThingA());給出了此錯誤handle(new ModifiedThingA());

x.java:6: <T,X>handle(X) in x cannot be applied to (x.ModifiedThingA)
            handle(new ModifiedThingA());
            ^

Java 7甚至不喜歡handle(new ThingA()); ,這是Java 7的輸出:

x.java:5: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ThingA());
                  ^
    inferred: ThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
x.java:6: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
            handle(new ModifiedThingA());
                  ^
    inferred: ModifiedThingA
    bound(s): CAP#1
  where T,X are type-variables:
    T extends BaseThing<T> declared in method <T,X>handle(X)
    X extends T declared in method <T,X>handle(X)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends BaseThing<CAP#1> from capture of ?
2 errors

這在我看來, javac被誤認為ModifiedThingABaseThing<ModifiedThingA>時,它實際上是一個BaseThing<ThingA> 這是我的bug還是javac

javac的行為似乎是正確的。 從理論上講,一個類型變量T就足夠了。 但是,您引入了第二個類型變量X ,以幫助進行類型推斷。 首先推斷X的參數,然后根據調用上下文推斷T的參數:

List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());

但是,您的調用上下文不會對返回類型施加任何限制。 因此,編譯器被迫引入以空類型為下限的類型變量( CAP#1 )。 T的參數將推斷為glb(BaseThing<CAP#1>) = BaseThing<CAP#1> 給定其下限, X不能證明是T的子類型。

有兩種或三種方法可以解決此問題。

  1. 手動推斷(如果很少需要,可以)
  2. 提供返回類型為void的“過載”(需要另一個名稱,urgh)
  3. 如果返回的列表是不可變的或防御性的副本,則可以將類型參數T與它的bounds參數斷開連接

我更喜歡選項3:

private <T extends BaseThing<T>> List<T> handle(BaseThing<? extends T> object) {
    return new ArrayList<T>(object.getList());
    // or (using guava's ImmutableList)
    return ImmutableList.copyOf(object.getList());
}

快樂的仿制葯。

您的代碼可以在javac 1.8.0_45和Eclipse 4.1.1中很好地編譯。

為了更好地編譯lambda表達式,對Java 8中的類型推斷算法所做的更改可能也解決了您的問題。

javac 1.7.0_09和1.7.0_11中似乎存在一個錯誤,這會導致此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM