簡體   English   中英

Kotlin - 無法創建兩個具有不同列表類型參數的構造函數

[英]Kotlin - Can't create two constructors with different List types parameters

我試圖創建以下類:

class MyClass {

    var foos: List<Foo> = listOf()

    constructor(foos: List<Foo>) {
        this.foos = foos
    }

    constructor(bars: List<Bar>) : super() {
        this.foos = bars.map { bar ->
            Foo(bar)
        }
    }
}


但是,我收到一條錯誤消息:

平台聲明沖突:以下聲明具有相同的 JVM 簽名( (Ljava/util/List;)V):


我知道它們都是 List 對象,但它們是用泛型鍵入的,所以我確信這不會有問題。

您會遇到這個問題,因為在 Java 中存在稱為類型擦除的東西。 並且因為 kotlin 使用 JVM,所以它也受此限制的影響。 給 TL;DR;

泛型類型保留在.class文件中,因此 java 知道該類(在您的情況下為List )是泛型的。 但它無法跟蹤實例的泛型類型。 因此實例List<Foo>List<Bar>在運行時都以其原始類型形式處理( List )。 請記住,泛型僅在編譯時使用以確保類型安全。

為了克服這個限制,您可以在 kotlin 中使用運算符重載。 我們正在查看的運算符是() ,它讓您可以調用任何實例。 通過使用companion object我們甚至可以使這個invoke看起來像一個構造函數,並且像一個構造函數一樣被調用( MyClass() )。 您的代碼可能如下所示:

class MyClass(var foos: List<Foo>) {
    companion object {
        operator fun invoke(bars: List<Bar>) = MyClass(bars.map(::Foo))
    }
}

這允許您像這樣簡單地調用它:

val mc1 = MyClass(foos) // calls constructor
val mc2 = MyClass(bars) // calls companion.invoke

這不是 Kotlin 問題,而是 Java 泛型的一種機制。 此機制旨在避免仍然使用原始類型的遺留代碼中的沖突。

例如,如果您在 Java 中創建一個類,如下所示:

public class MyClass {

    private List<Foo> foos;

    MyClass(List<Foo> foos) {
        this.foos = foos;
    }

    MyClass(List<Bar> bars) {
        List<Foo> foos = new ArrayList<>();
        bars.forEach(bar -> foos.add(new Foo(bar)));
        this.foos = foos;
    }
}


你會得到一個編譯時錯誤,說:

'MyClass(List)' 帶有 'MyClass(List)' 的類; 兩種方法具有相同的擦除


類型擦除的作用是僅在編譯時強制執行類型約束,並在運行時丟棄元素類型信息。 如果類型參數未綁定,則類上的類型參數在代碼編譯期間會被丟棄,並替換為其第一個綁定或Object 因此,兩個構造函數(因為它們都是無界的)都屬於 List,因此會出現錯誤。

正如其他人提到的,這是由於 Java 的類型擦除。 在某些情況下,伴隨對象方法有效,但在其他情況下則不然。

如果您只有少數構造函數發生沖突,我使用的一種解決方法是利用 Java 的繼承。

例如,你可以這樣做:

class MyClass {
    constructor(foos: Collection<Foo>) {
        this.myObjects = parseFoos(foos)
    }

    constructor(bars: List<Bar>) {
        this.instructions = parseBars(bars)
    }

    constructor(bazs: ArrayList<Baz>) {
        this.instructions = parseBazs(bazs)
    }
}

請注意參數更改( CollectionListArrayList )。 您的錯誤應該消失了,您應該能夠傳入FooBarBaz ArrayList 對象,Java 將調用正確的構造函數。

同樣,您只能對少數構造函數執行此操作,並且如果您願意對調用構造函數的參數類型進行一些調整(例如,將它們聲明為ArrayList而不是List )。

暫無
暫無

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

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