Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I was trying to create the following class:

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)
        }
    }
}


However, I get an error saying:

Platform declaration clash: The following declarations have the same JVM signature ( (Ljava/util/List;)V):


I understand that they are both List objects, but they are typed with generics so I was sure it would not be a problem.

like image 587
Leonardo Sibela Avatar asked May 30 '19 20:05

Leonardo Sibela


People also ask

Can you have 2 constructor with different names?

There can be multiple constructors in a class. However, the parameter list of the constructors should not be same. This is known as constructor overloading.

Can we have multiple constructors in Kotlin?

A Kotlin class can have a primary constructor and one or more additional secondary constructors.

Can you have two constructors with the same number and type of parameters?

The technique of having two (or more) constructors in a class is known as constructor overloading. A class can have multiple constructors that differ in the number and/or type of their parameters. It's not, however, possible to have two constructors with the exact same parameters.


1 Answers

You encounter this problem because in java there exists something called type erasure. And because kotlin uses the JVM it is also affected by this limitation. To give a TL;DR;

The generic type is retained in the .class file so java knows that the class (in your case List) is generic. But it can't keep track of the generic type of an instance. So instances List<Foo> and List<Bar> are both handled in their raw type form at runtime (List). Keep in mind that generics are only used at compile time to ensure type safety.

To overcome this limitation, you can make use of operator overloading in kotlin. The operator we're looking at is () which let's you invoke any instance. By using a companion object we can even make this invoke look like a constructor, and be invoked like one (MyClass()). Your code can look like this:

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

Which allows you to call it simply like this:

val mc1 = MyClass(foos) // calls constructor
val mc2 = MyClass(bars) // calls companion.invoke
like image 111
Lino Avatar answered Sep 23 '22 02:09

Lino