In Kotlin, the following code compiles:
class Foo { fun bar(foo: List<String>): String { return "" } fun bar(foo: List<Int>): Int { return 2; } }
This code, however, does not:
class Foo { fun bar(foo: List<String>): String { return "" } fun bar(foo: List<Int>): String { return "2"; } }
Compiling this will cause the following error:
Error:(8, 5) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (foo(Ljava/util/List;)Ljava/lang/String;): fun foo(layout: List<Int>): String fun foo(layout: List<String>): String
In Java, neither example will compile:
class Foo { String bar(List<Integer> foo) { return ""; } Integer bar(List<String> foo) { return 2; } } class Foo { String bar(List<Integer> foo) { return ""; } String bar(List<String> foo) { return "2"; } }
Unsurprisingly, both of the prior snippets generate the familiar compiler error:
Error:(13, 12) java: name clash: bar(java.util.List<java.lang.String>) and bar(java.util.List<java.lang.Integer>) have the same erasure
What surprises me is that the first Kotlin example works at all, and second, if it works, why does the second Kotlin example fail? Does Kotlin consider a method's return type as part of its signature? Furthermore, why do method signatures in Kotlin respect the full parameter type, in contrast with Java?
Type Erasure. As with Java, Kotlin's generics are erased at runtime. That is, an instance of a generic class doesn't preserve its type parameters at runtime. For example, if we create a Set<String> and put a few strings into it, at runtime we're only able to see it as a Set.
Type erasure The type safety checks that Kotlin performs for generic declaration usages are done at compile time. At runtime, the instances of generic types do not hold any information about their actual type arguments. The type information is said to be erased.
Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.
"Out" keyword is extensively used in Kotlin generics. Its signature looks like this − List<out T> When a type parameter T of a class C is declared out, then C can safely be a super type of C<Derived>. That means, a Number type List can contain double, integer type list.
Actually Kotlin knows the difference between the two methods in your example, but jvm will not. That's why it's a "platform" clash.
You can make your second example compile by using the @JvmName
annotation:
class Foo { @JvmName("barString") fun bar(foo: List<String>): String { return "" } @JvmName("barInt") fun bar(foo: List<Int>): String { return "2"; } }
This annotation exists for this very reason. You can read more in the interop documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With