簡體   English   中英

為什么我們不能在泛型類中創建一個“Concrete”類數組?

[英]Why can't we create an array of “Concrete” class inside a generic class?

public class GenericClass<T> {

    class MyClass {
    }

    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();                       // OK
        MyClass[] myArray = { new MyClass(), new MyClass() };   // Cannot create a generic array of GenericClass<T>.MyClass
    }
}

這不是創建通用數組。 編譯器應該沒有理解/確定MyClass問題,不是嗎?

內部類“知道”封閉類的哪個實例創建它們,並且可以訪問該實例的字段/成員。 就好像它們有第二個this變量,其類型是封閉類的具體類型(例如GenericClass<String> )。

為了克服這種困境,你可以使MyClass static 這將使它完全解耦封閉類的任何實例(即:它不會有第二this ),使他們能夠自由地進行實例化:

public class GenericClass<T> {

  static class MyClass {
  }

  public GenericClass(final T[] param) {
    MyClass myObject = new MyClass();                       // OK
    MyClass[] myArray = { new MyClass(), new MyClass() };   
  }
}

這是一些額外的信息 從鏈接......

Java數組攜帶運行時類型信息,用於標識所包含元素的類型

對於編譯器,您的代碼如下所示:

MyClass[] myArray = {new GenericClass<T>.MyClass(), ..} //T is unknown
{ new MyClass(), new MyClass() }; //new MyClass() => new GenericClass<T>.MyClass()

上面的代碼將被視為對象數組,因為T是未知的,由於實現泛型的方式(通過擦除),數組的類型沒有明確定義 一方面,它應該是一個MyClass數組,另一方面,它應該是一個Object數組

創建對象類型數組並將其強制轉換為您的類型

Object[] arr=new Object[]{this.new MyClass(), this.new MyClass()};
MyClass[]  myArray = Arrays.copyOf(arr,arr.length, Item.MyClass[].class);   

如果你將它設置為靜態它將起作用,因為- 靜態嵌套類或嵌套接口(順便說一下,它始終是靜態的)除了命名空間嵌套和訪問私有變量之外與它的外部類(或接口)沒有關系 作為標准API中的示例,查找嵌套在Map接口內的接口Map.Entry,但是無法訪問其類型參數,需要再次聲明它們。

涵蓋此內容的JLS部分是10.6 具體來說,這是因為:

如果ClassOrInterfaceType不表示可重新類型(第4.7節),則為編譯時錯誤。 否則,ClassOrInterfaceType可以命名任何命名引用類型,甚至是抽象類類型(第8.1.1.1節)或接口類型(第9節)。

上述規則意味着數組創建表達式中的元素類型不能是參數化類型,而不是無界通配符。

因為MyClass是非靜態的,所以它依賴於外部類; 它實際上是GenericClass<T>.MyClass ,因此是參數化類型。 將其聲明為static會刪除該依賴項並解決問題。

如果你這樣做,那會很奇怪;

class MyClass<T> {
}

public GenericClass(final T[] param) {
    MyClass[] myArray = { new MyClass(), new MyClass() };  
}

這是合法的。 笨拙,有點笨拙但合法。 因為你重新聲明了類型,它隱藏了外部類型。 然后...數組和泛型不混合......除非你使用原始類型。 為了向后兼容,您可以擁有一個rawtype數組,最終持有MyClass<Object> 這是一個非常糟糕的事情,但它確實編譯。 你可以在這里擺脫創意鑄造,但最終......只是......不要。

這里的問題是編譯器無法在編譯時確定數組myArray的信息。 它被認為是通用的,因為(正如eclipse所示)它在{new GenericClass <T> .MyClass(),...}中轉換。 這是因為您將類MyClass放在泛型類中。

此代碼也不起作用:

package my.stuff;

public class GenericClass<T> {

    class MyClass {
        static MyClass[] myArray = { new MyClass(), new MyClass() };;
    }

    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();
    }
}

但是這段代碼有效:

package my.stuff;

public class GenericClass<T> {
    public GenericClass(final T[] param) {
        MyClass myObject = new MyClass();
        MyClass[] myArray = { new MyClass(), new MyClass() };
    }
}

class MyClass {
}

因為你沒有在你的MyClass中使用泛型,所以最好的辦法可能是第二個。

如果將其聲明為靜態,則編譯器知道MyClass不是通用的,並且它知道該怎么做。

此外,在java中創建泛型數組的唯一方法是創建一個原始類型,然后將其強制轉換為泛型(參見此處: “無法創建...的通用數組” - 如何創建Map <String,Object>的數組? )。 所以,如果你絕對需要myClass里面的myClass,你應該在MyClass <T>中轉換它,然后你使用技巧:創建一個原始類型並將它轉換為MyClass <T>:

package my.stuff;

public class GenericClass<T> {

    class MyClass<T> {
    }

    @SuppressWarnings("unchecked")
    public GenericClass(final T[] param) {
        MyClass<T> myObject = new MyClass<T>();
        MyClass<T>[] myArray = new MyClass[]{ new MyClass<T>(), new MyClass<T>() };
    }
}

即使你不在MyClass類中使用T.

@ItayMaman有正確的理由。 基本上, MyClass 不是可再生的類型。

MyClass是一個非靜態的內部類。 由於它是非靜態的,因此它在其封閉類的類型參數范圍內。 每次在GenericClass的實例方法中自己編寫MyClass ,它實際上都是GenericClass<T>.MyClass縮寫。 因此,即使它看起來不像, MyClass (本身)實際上是一個參數化類型(由T參數化),類似於List<String> 所以當你做new MyClass[2] ,你試圖創建一個參數化類型的數組,就像new List<String>[2] 我想你已經知道這是不允許的。

你該怎么辦? 這一切都取決於你的意圖。 人們建議的一件事是讓MyClass靜態。 當然,這將超出T的范圍。 但這可能是也可能不是你想要的,因為它完全改變了它與GenericClass關系。 非靜態內部類可以訪問封閉類的實例,這也許就是為什么你首先這樣做的原因。 如果你從來沒有打算讓它成為非靜態的(並且錯誤地做了),那么這顯然是要走的路。

如果你想要一個非靜態內部類,並且你只想創建一個這種類型的數組,那么讓我們考慮你通常如何處理參數化類型的數組,例如List<String>[]

  • 一種解決方案是改為創建原始類型的數組,例如List[] foo = new List[2]; 對我們的案例執行此操作的等效方法是GenericClass.MyClass[] foo = new GenericClass.MyClass[2]; 注意我們在這里做了什么。 為了編寫原始類型,我們必須使用未參數化的外部類名明確限定MyClass 如果我們沒有明確限定它,那么它將被GenericClass<T>隱式限定,如上所述,這不是我們想要的。 將此轉換為示例中的代碼,您將編寫GenericClass.MyClass[] myArray = { new MyClass(), new MyClass() };

  • 類似地,如果我們想要避免原始類型,我們可以創建一個通配類型的數組,例如List<?>[] foo = new List<?>[2]; 對我們的案例執行此操作的等效方法是GenericClass<?>.MyClass[] foo = new GenericClass<?>.MyClass[2]; 因此,將其轉換為示例中的代碼,您將編寫GenericClass<?>.MyClass[] myArray = { new MyClass(), new MyClass() };

  • 最后,我們可能想要創建一個通配類型的數組,但后來轉換回參數化類型的數組,以方便以后使用。 例如List<String>[] foo = (List<String>[])new List<?>[2]; 對我們的案例執行此操作的等效方法是MyClass[] myArray = (MyClass[])new GenericClass<?>.MyClass[] { new MyClass(), new MyClass() }; 請注意,演員表是未選中的演員。 這樣做的好處是,當你從myArray獲取東西時,它將是MyClass類型,而不是原始類型GenericClass.MyClass或通配類型GenericClass<?>.MyClass來自上述方法的GenericClass<?>.MyClass

暫無
暫無

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

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