Kotlin default arguments in interface bug? - kotlin

kotlin file
interface Test {
fun test(message: String, delay: Int =100)
}
class A: Test
{
override fun test(message: String, delay: Int) {
}
}
I find i can't use #JvmOverloads in interface nor class.
if i add a #JvmOverloads in interface,the error is #JvmOverloads annotation cannot be used on interface method,if i add #JvmOverloads in class,the error is platform declaration clash....
However, I seem able to use defaults paramters in kotlin files,like this.
var a=A()
a.test("1234")
But when I use it in a java file, it seems that the method is not overloaded。
A a=new A();
a.test("123");//Compile error
The following version without interface can work
class A
{
#JvmOverloads
fun test(message: String, delay: Int=100) {
}
}
Then I can use it normally in java file
A a=new A();
a.test("123");
But how to maintain the same functionality after add the interface?

This is not a bug. #JvmOverloads annotation simply does not work with abstract methods.
From Kotlin docs:
Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the #JvmOverloads annotation.
The annotation also works for constructors, static methods etc. It can't be used on abstract methods, including methods defined in interfaces.
source: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#overloads-generation
Why?
Because as You can learn from the doc I mentioned, #JvmOverloads instructs compiler to generate bunch of Java overloaded methods, omitting each of the parameters one by one, starting from the last one.
As far as I understand, each overloaded method calls internally method with one more parameter, and this additional parameter has default value. Edit: see comment by #hotkey here
This won't work with abstract methods, because they don't have any body.
Also new Java interface would have more methods, and its implementations would have to implement all of them. Kotlin interface had only one method.

To get to the same result you can make a LegacySupport class in Kotlin that will actually call the function with the default parameter and then you can expose only the return of the function to the java class from this class.

Related

Generic constraint for "data" class objects

I would like to semantically constrain a map to only accept "data" class object types as the value in kotlin like so:
class Test(
val test : Int
)
data class Test2 (
val test : Int
)
fun test(map : Map<String, /* compile error on Test, but accept Test2 or any other data class */>) {
}
I'm mainly trying to do this so that I can keep everything in the map cloneable, but when I do this:
fun <T: Cloneable> test(map : Map<String, T>) {
// test clone
map.map { it.key.uuid to it.value.clone() } .toMap() // error on .clone() Cannot access 'clone': it is protected in 'Cloneable'
}
but I thought implementing the Cloneable interface made your clone method public? Essentially I'm looking for a compile time guarantee that all data is copyable in that method invocation, (is a primitive type, a data class that I can call .copy() on, or any object that has implemented Cloneable). Is my only option reflection and runtime assertions?
I thought implementing the Cloneable interface made your clone method public?
No, it's simply a marker interface, which tells the protected Object.clone() method not to throw a CloneNotSupportedException.  In practice, classes that implement Cloneable will usually override clone() and make it public, but that's not necessary.  And of course that's no help when you don't know the exact type!
The cloning mechanism was an early part of Java, and not very well-designed.  (Effective Java calls it “a highly atypical use of interfaces and not one to be emulated”.)  But it's still used, so we're stuck with it…
(See also these related answers.)
I don't know whether this is the best way or not, but how about you to use property like below.
SomeClass::class.isData
Kdoc says
true if this class is a data class.

Dagger2 - How to use #Named with #BindsInstance

How is #Named used with #BindsInstance? I have the this component
interface AppComponent : AndroidInjector<MyApplication>{
#Component.Builder
abstract class Builder : AndroidInjector.Builder<MyApplication>() {
#BindsInstance
abstract fun preferenceName( #Named("PreferenceName") name : String ) : Builder
}
}
and trying to inject in MyApplication
#Inject
#Named("PreferenceName")
lateinit var prefName : String
But it fails with MissingBinding for String. I could resolve this with a module provider but trying to avoid provider for constants.
Update: Dagger 2.25.2 has eliminated the need for workaround:
Kotlin support
ii. Qualifier annotations on fields can now be understood without
The need for #field:MyQualifier (646e033)
iii. #Module object classes no longer need #JvmStatic on the
provides methods. (0da2180)
This doesn't have anything to do with #BindsInstance, but rather the #Named annotations on fields. You can tell from the "MissingBinding for String", which would otherwise give you an error about a Named string.
As in Svetlozar Kostadinov's article Correct usage of Dagger 2 #Named annotation in Kotlin, you'll need to clarify to Kotlin that you'd like the annotations to apply to the field.
#field:[Inject Named("PreferenceName")]
lateinit var prefName : String;
As Svetlozar puts it:
The reason is because in Kotlin annotations need to be slightly more complicated in order to work as expected from Java perspective. That’s coming from the fact that one Kotlin element may be a facade of multiple Java elements emitted in the bytecode. For example a Kotlin property is a facade of an underlying Java member variable, a getter and a setter. You annotate the property but what Dagger expects to be annotated is the underlying field.
Related: Dagger 2 constructor injection in kotlin with Named arguments

override and implement fn from class in interface

I want to override toString() in an interface and have objects that implement that interface to default to using that method (eg: not shadowed)
interface SingletonObjectTrait {
fun toString(): String = this.javaClass.simpleName
}
Is there a straightforward way to define such an interface, preferably with minimal configuration at implementation
object MyEvent: SomeEventLogic(), SomeEventType, SingletonObjectTrait
class SomeEventLogic {}
interface SomeEventType {}
That's not possible, I'm afraid.
Method implementations in interfaces work much like default methods in Java: they're used only if the implementing class doesn't have an implementation already.  But every class already inherits toString() from Any, so the default would never be used.
In fact, the compiler has a specific error for this — if you try to implement toString() in an interface, it says:
An interface may not implement a method of 'Any'
I can't see a good way around this.
As Erik says, one option is to change the interface to an abstract class; but of course that's not viable if any implementations already extend another class.
Another option might be to implement a different method in the interface, and in the comments instruct implementing classes to override toString() and call that method.  Not automatic, but less work for implementers, and less repetition.
There isn't a great way to do this other than using maybe an annotation processor to add the missing override at compile time (by adding an annotation to the interface that you detect and you generate the overrides in the implementation class). This would work, but may be biting off more than you want to, and is likely out of scope of an answer here on Stack Overflow (seek "how to write an annotation processor for Java or Kotlin" and "KAPT" for the one engine that supports Kotlin processors).
Back to your code and why it will not work as-is:
An interface cannot have a method with a signature that matches any of the methods in Any class. And if you try to override one of them you will get a compilation error.
An interface may not implement a method of 'Any'
The minimal code to do something like you want is:
interface SingletonObjectTrait {
fun asString(): String = this.javaClass.simpleName
}
open class SomeEventLogic {}
interface SomeEventType {}
object MyEvent: SomeEventLogic(), SomeEventType, SingletonObjectTrait {
override fun toString(): String = asString() // hope they don't forget to call this!
}
There is no guarantee the implementer will call the trait asString() function but at least you can share the functionality with a one-liner in the implementing class.

Can extension functions be called in a "static" way?

Is it possible to create an extension function and call it as if it were static?
For Example...
fun System.sayByeAndExit() {
println("Goodbye!")
System.exit()
}
fun main(args: Array<String>) {
System.sayByeAndExit() // I'd like to be able to call this
}
I know that the code sample doesn't work...
I understand that kotlin's extension functions are resolved statically, as mentioned in the Kotlin Reference (Extension Functions), but this does not mean they can be called as if they were static functions within a class (in a Java sense).
I also understand that this code will not work because there is no instance of System to pass into the method that the compiler will generate; therefore it won't compile.
Why would I want this?
Some of you might be wondering why this behaviour is desirable. I can understand why you would think that is isn't, so here are some reasons:
It has all of the benefits that standard extension functions give.
An instance of the class doesn't need to be created just to access the extra functionality.
The functions can be accessed from an application-wide context (provided the class is visible).
To summarise...
Does Kotlin have a way to "hook" a static function onto a class? I'd love to know.
You are really asking for "extension functions for a Class reference" or "adding static methods to existing classes" which was covered by another question here: How can one add static methods to Java classes in Kotlin which is covered by a feature request KT-11968
Extension functions cannot be added to anything that does not have an instance. A reference to a Class is not an instance and therefore you cannot extend something like java.lang.System. You can however extend a companion object of an existing class. For example:
class LibraryThing {
companion object { /* ... */ }
}
Allows you to extend LibraryThing.Companion and therefore calling some new myExtension() method would look like you are extending the Class reference itself, when really you are extending the singleton instance of the companion object:
fun LibraryThing.Companion.myExtension() = "foo"
LibraryThing.Companion.myExtension() // results in "foo"
LibraryThing.myExtension() // results in "foo"
Therefore you might find some Kotlin libraries add empty companion objects just for this case. Others do not, and for those you are "out of luck." Since Java does not have companion objects, you cannot do the same for Java either.
The other commonly requested feature is to take an existing Java static method that accepts an instance of a class as the first parameter, and make it behave as an extension function. This is tracked by issues KT-5261, KT-2844, KT-732, KT-3487 and probably other feature requests.
You can define extension function for an object and use it from system-wide context. An object will be created only once.
object MyClz
fun MyClz.exit() = System.exit(0)
fun main(args: Array<String>) {
MyClz.exit()
}
Or
class MyClz {
companion object
}
fun MyClz.Companion.exit() = System.exit(0)
fun main(args: Array<String>) {
MyClz.exit()
}

How can one add static methods to Java classes in Kotlin

Is it possible to add a new static method to the java.lang.Math class in Kotlin? Usually, such things are possible in Kotlin thanks to Kotlin Extensions.
I already tried doing the following in a file I made called Extensions.kt:
fun Math.Companion.clamp(value:Double,minValue:Double,maxValue:Double):Double
{
return Math.max(Math.min(value,maxValue),minValue)
}
but Math.Companion could not be resolved...
As of Kotlin 1.3, this is not possible. However, it's being considered for a future release!
To help this feature get implemented, go vote on this issue: https://youtrack.jetbrains.com/issue/KT-11968
Because all proposals are basically in limbo right now, I wouldn't hold my breath that this will get in any time soon
I think this is not possible. Documentation says the following:
If a class has a companion object defined, you can also define extension functions and properties for the companion object.
The Math class is a Java class, not a Kotlin one and does not have a companion object in it. You can add a clamp method to the Double class instead.
As of Kotlin 1.2 it is still not possible.
As a workaround, to statically "extend" Environment class I am currently using:
Class EnvironmentExtensions {
companion object {
#JvmStatic
fun getSomething(): File {
...
return Environment.something()
}
}
}
It is not an ideal solution but IntelliJ/Android Studio code completion helps with the usage:
val something = EnvironmentExtensions.getSomething()