"same JVM signature" implementing kotlin interface containing getter method - intellij-idea

interface MyInterface {
fun getTheString(): String
}
class MyClass(var theString: String) : MyInterface {
...
}
normally when I have a variable in the constructor for a class, it creates a getter and setter for that variable. In MyClass, the methods getTheString() and setTheString(String) exist when not implementing MyInterface.
When MyClass implements MyInterface, I get the error:
Accidental override: The following declarations have the same JVM signature (getTheString()Ljava/lang/String;):
public final fun (): String defined in MyClass
public abstract fun getTheString(): String defined in MyClass
I also have the error: Class 'MyClass' is not abstract and does not implement abstract member public abstract fun getTheString(): String defined in MyInterface.
So I have a few questions:
Why are 2 getter methods getting generated with the same JVM signature when implementing the interface versus one getter method getting generated without implementing the interface?
Why is it complaining I haven't implemented a getTheString() method when this method is automatically generated by kotlin?
How can I get the getter generated by the variable to become the implementation of the method in the interface?

If the interface is indeed in Kotlin, and you can change it, it should be
interface MyInterface {
val theString: String
}
in the first place. Java will still see getTheString(), but it's nicer to both implement and use in Kotlin.
Otherwise a good option is
class MyClass(#set:JvmName("setTheString") var _theString: String) : MyInterface {
override fun getTheString() = _theString
}
Unfortunately, it still has a duplicate getter, and you can't make only the getter private. Or
class MyClass(private var _theString: String) : MyInterface {
override fun getTheString() = _theString
fun setTheString(value: String) {
_theString = value
}
}
Note that if the interface is in Java, getTheString() will be visible to Kotlin as a property.
See issues https://youtrack.jetbrains.com/issue/KT-6653 and https://youtrack.jetbrains.com/issue/KT-19444 on the Kotlin bug tracker.

Related

How to call an abstract method from a Class parameter in Kotlin?

Aim
Have a function Book, which takes one of three Letter classes as argument myClass and then calls 'genericMethod()' from the abstract class which Letter*() has inherited.
Issue
If I try Book(LetterA()).read() I get the following error:
Type mismatch. Required: Class<SampleClassArguments.Alphabet> Found: SampleClassArguments.LetterA
Does Kotlin have any way to achieve this result?
Code
#Test
fun readBookTest() {
Book(LetterA()).read() /*<--error here*/
}
class Book(val myClass: Class<Alphabet>) {
fun read() {
val letterClass = myClass.getConstructor().newInstance()
letterClass.genericMethod(myClass.name)
}
}
class LetterA(): Alphabet()
class LetterB(): Alphabet()
class LetterC(): Alphabet()
abstract class Alphabet {
fun genericMethod(className: String) {
println("The class is: $className")
}
}
You need to define the Class type as covariant with the out keyword so any of the child classes is an acceptable argument:
class Book(val myClass: Class<out Alphabet>)
And when you use it, you need to pass the actual Class, not an instance of the class. You can get the Class by calling ::class.java on the name of the class:
#Test
fun readBookTest() {
Book(LetterA::class.java).read()
}

Default value for generic member

I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}

Parcelable overload resolution ambiguity

I am trying to create a POJO (aka data classes in Kotlin) structure of a JSON response in Kotlin. I've implemented the Parcelable interface for each data class in the structure. In all of the data classes, I've auto generated the Parcelable implementation. The issue is the generated second constructor where the IDE is complaining about:
Overload resolution ambiguity
It states that it's being confused between these two constructors:
public constructor GeocodeRes(parcel: Parcel)
public constructor GeocodeRes(responset: ResponseRes)
Which I believe makes sense because ResponseRes is also of type Parcelable (ResponseRes implements Parcelable). So calling the GeocodeRes(parcel) method (within the createFromParcel companion method), it is getting confused.
That was until I removed ResponseRes from implementing the Parcelable class and it's still showing the same error.
Is there any reason to this? Am I setting this up properly? In all of the children data classes, they all implement the Parcelable interface (with dependence with eachother) but aren't running into any issues.
Here's my GeocodeRes class:
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
data class GeocodeRes(
#SerializedName("Response") #Expose val responset: ResponseRes
) : Parcelable {
// this is the problem. the IDE is complaining that the usage is too ambiguous (). however, the only usage of this constructor is within this class - just doesn't tell me where exactly.
constructor(parcel: Parcel) : this(parcel.readParcelable(ResponseRes::class.java.classLoader)) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(responset, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GeocodeRes> {
override fun createFromParcel(parcel: Parcel): GeocodeRes {
return GeocodeRes(parcel)
}
override fun newArray(size: Int): Array<GeocodeRes?> {
return arrayOfNulls(size)
}
}
}
Here's my ResponseRes class:
data class ResponseRes(
#SerializedName("MetaInfo") #Expose val metaInfo: MetaInfo,
#SerializedName("View") #Expose val views: List<View>
): Parcelable
{
[...]//parcel methods
}
however, the only usage of this constructor is within this class - just doesn't tell me where exactly
The problem is with the definition itself, not with any usage. It could never be used, and the error would still be there.
You should be able to fix this by specifying which Parcelable you want to read:
this(parcel.readParcelable<ResponseRes>(ResponseRes::class.java.classLoader))
The compiler can't decide if you mean that or
this(parcel.readParcelable<Parcel>(ResponseRes::class.java.classLoader))
Even though the second wouldn't be legal because Parcel doesn't implement Parcelable, if you look at the signature
<T extends Parcelable> T readParcelable(ClassLoader loader)
you can see only the return type can be used to infer T, not the argument. So the compiler need to pick the constructor overload before trying to infer T.

Static Instance of Interface as part of the interface

Imagine I had an interface like:
interface MyInterface {
fun doSomething()
}
And I was interop-ing between Kotlin and Java. I now want a constant static instance of this interface but I want that to be part of the interface. I could do this:
interface MyInterface {
fun doSomething()
companion object {
val CONSTANT = object: MyInterface {
override fun doSomething() { ... }
}
}
}
but that means I need to write MyInterface.Companion.getCONSTANT(). #JvmField doesn't work here.
I've also tried:
interface MyInterface {
fun doSomething()
object CONSTANT: MyInterface {
override fun doSomething() { ... }
}
}
}
Which works in other Kotlin files (I can write MyInterface.CONSTANT) but I'd have to write MyInterface.CONSTANT.INSTANCE in Java. This solution seems the closest to what I want.
Any solutions? I want to be able to write MyInterface.CONSTANT in both Kotlin and Java and have them refer to a single static final object that implements the interface.
I believe I could also convert my Interface to an abstract class but that's the last resort.
The issue of not being able to use #JvmStatic in interfaces is tracked in this ticket: https://youtrack.jetbrains.com/oauth?state=%2Fissue%2FKT-6301
It is fixed by now and one comment says
Fix would be avaliable in 1.2.30 under '-language-version 1.3' option

TypeScript function signature in abstract class doesn't need to match interface

In the following example, interface IFoo declares a function signature requiring two number arguments. Abstract class BaseFoo implements this interface, but declares the function with a different signature. Finally, concrete class Foo extends BaseFoo and implements BaseFoo's version of the function declaration.
interface IFoo {
func(x: number ): number
}
abstract class BaseFoo implements IFoo {
abstract func(x: number): number
}
class Foo extends BaseFoo {
func() { return -1 } // Does not match interface func declaration
}
let foo: IFoo = new Foo() // Should not be able to instantiate a Foo as an IFoo
let y = foo.func() // Should not be able to call without an argument
console.log(y)
This contrived example illustrates something that happened in real life: I had an existing interface in a codebase. I updated one of it's function's signatures, with the expectation that the compiler would help me find all the classes who would need to be updated. But, no errors.
Why am I allowed to instantiate an abstract class with a function signature that doesn't match the interface?