簡體   English   中英

為什么Java允許將Object類型的表達式顯式轉換為A <B<C> &gt;,類型A <?> 到A <B<C> &gt;但不是A型 <B<?> &gt;至A <B<C> &gt;?

[英]Why does Java allow the explicit conversion of expressions of type Object to A<B<C>>, of type A<?> to A<B<C>> but not of type A<B<?>> to A<B<C>>?

Java將允許我這樣做:

public static class SomeType<I>{}

private static Map<Class<?>, Object> m = new HashMap<Class<?>, Object>();

public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
     return (List<SomeType<X>>)m.get(clazz);//warning
}

它還可以讓我做到這一點:

public static class SomeType<I>{}

private static Map<Class<?>, List<?>> m = new HashMap<Class<?>, List<?>>();

public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
    return (List<SomeType<X>>)m.get(clazz);//warning
}

但這不會讓我這樣做:

public static class SomeType<I>{}

private static Map<Class<?>, List<SomeType<?>>> m = new HashMap<Class<?>, List<SomeType<?>>>();

public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
    return (List<SomeType<X>>)m.get(clazz);//will not compile
}

除非我采用以下解決方法:

public static class SomeType<I>{}

private static Map<Class<?>, List<SomeType<?>>> m = new HashMap<Class<?>, List<SomeType<?>>>();

public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
    return (List<SomeType<X>>)(Object)m.get(clazz);//warning
}

因此,java使得可以將從顯式轉換為Object到A<B<C>> ,從A<?>A<B<C>>但不能從A<B<?>>A<B<C>>

這是為什么?

看來,您的第三個示例違反了第一個JLS 5.5.1規則

如果S是類類型:

如果T是類類型,那么| S | <:| T |或| T | <:| S |。 否則,將發生編譯時錯誤。

此外,如果存在T的超類型X和S的超類型Y,以致X和Y都是可證明是截然不同的參數化類型(第4.5節),並且X和Y的擦除相同,則編譯時發生錯誤。

實際上,令SList<SomeType<?>>>TList<SomeType<X>> 這些ST是可證明是截然不同的參數化類型,因為<?>不等於<X> 同時,它們的擦除相同,即: ListList

因此,根據規格,這會導致編譯時錯誤。

當您第一次將m.get(...)Object ,您不會違反提到的條件: ObjectList<SomeType<X>>沒有相同的擦除,此外, |List<SomeType<X>>| <: |Object| |List<SomeType<X>>| <: |Object|

PS對於List<?>情況,這也沒有違反提到的規則,因為|List<SomeType<X>>| <: |List<?>| |List<SomeType<X>>| <: |List<?>|

Java不會編譯可證明無法成功的類型轉換 (假設事物是它們被聲明為的類型,並假定該值不為null )。 為了使類型轉換成功,從理論上講,必須有一個非空類型,它是這兩種類型的子類型。

  • ObjectA<B<C>> :這是可能的這一成功。 例如,類型A<B<C>>是兩者的子類型。

  • A<?>A<B<C>> :這有可能成功。 例如,類型A<B<C>>是兩者的子類型。

  • A<B<?>>A<B<C>> :不可能成功。 即,不能存在作為兩者的子類型的類型。

要了解為什么要使用最后一個,請回想一下對於參數化類型,如果AB不同,並且Foo<A>都不是Foo<B>的子類型,並且都不是通配符。 因此,考慮A<B<?>> 它的參數B<?>不是通配符(它是實際類型;它不是?? extends something? super something )。

因此,唯一可以作為A<B<?>>子類型的類型是本身,以及SubclassOfA<?B<?>> 同樣的情況也適用於A<B<C>> :唯一可以成為A<B<C>>的子類型的類型本身就是SubclassOfA<?B<C>>

那么,您能否看到不可能同時擁有兩者的子類型呢?

因為它不是類型安全的。 一個例子:

List<Class<?>> classes = new ArrayList<Class<?>>();
classes.add(String.class);

// This is invalid!
List<Class<Boolean>> casted = (List<Class<Boolean>>) classes;
// We've somehow assigned a Class<String> to a Class<Boolean>!
Class<Boolean> invalid = casted.get(0);

此外,盡管Java允許從ObjectList<Class<Boolean>>的強制轉換以及從List<?>List<Class<Boolean>>的強制轉換,但兩者都會產生未經檢查的警告。 它們不是錯誤,因為可以想象它們是類型安全的,而List<Class<?>>List<Class<Boolean>>不能是類型安全的。

這個

public static <X> List<SomeType<X>> getList(Class<X> clazz)
{
    return (List<SomeType<X>>)m.get(clazz);//will not compile
}

不允許這樣做是因為,編譯器對SomeType一無所知。

您已經創建了帶有類型參數X和參數Class的方法,但是結果是您期望返回一個存儲X類型的SomeType的列表。 您沒有指定如何從X傳遞到SomeType。 獲取期望Object的映射,但返回聲明的類型的女巫是SomeType<?>

作為? 在這種情況下,它與X更不一樣,您可以執行以下操作。

為了解決這個問題,如果要在返回類型中使用它,則必須說X必須是。

public static <X extends SomeType<?>> List<? super X> getList(Class<X> clazz)
{
    return  m.get(clazz); //No error, no warring ;-)
}

暫無
暫無

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

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