繁体   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