Auto-generate lambda expression as argument in IntelliJ - intellij-idea

How do I get IntelliJ to auto-generate a lambda expression as the argument being passed?
What I want:
I have seen the Question How to autocomplete lambdas in IntelliJ IDEA?, but that does not seem to produce my desired result.

As Tagir Valeev already mentioned in his comment, IntelliJ will auto-generate lambda code, but only if your ClickListener is a (functional) interface.
E.g.:
#FunctionalInterface
interface ClickListener {
void listen();
}
class Clicker {
void addClickListener(ClickListener listener){
// ...
}
}
Then, Intellij suggests upon hitting Ctrl+Shift+Space
Note: As to the #FunctionalInterface annotation, Jav Doc says
This annotation is not a requirement for the compiler to recognize an interface as a functional interface

Related

Confusing Property Delegation in Gradle's Kotlin DSL

Below is the code snippet that I came across in gradle's documentation
https://docs.gradle.org/current/userguide/tutorial_using_tasks.html
val hello by tasks.registering {
doLast {
println("Hello Earth")
}
}
hello {
doFirst {
println("Hello Venus")
}
}
In the above, hello is a TaskProvider type which provides task definition/action. The second call to hello is to extend the behavior of the task.
This delegate use looks slightly confusing to me. Following are the questions which are bugging me:
1) On inspecting the decompiled byte-code, I see tasks.registering returns RegisteringDomainObjectDelegateProviderWithAction object which should be used as the delegate and hence should provide getValue() and setValue() methods for delegate to work but as I saw, methods are not provided. Instead the class RegisteringDomainObjectDelegateProviderWithAction has a delegateProvider property of type tasks which is supposed to provide the delegate. Can any one help me understand, how delegation works here?
2) The second call is supposed to add behavior to the hello task. Since hello is a property, how are we able to pass a lambda/behavior to it? What am I missing?
I have already seen kotlin documentation which provides good explanation of delegates but doesn't aid in understanding the above case https://kotlinlang.org/docs/reference/delegated-properties.html
I would appreciate a detailed explanation as I am new to Kotlin.
Regarding the delegate use:
The delegation works via an extension operator method provideDelegate defined on RegisteringDomainObjectDelegateProviderWithAction:
operator fun RegisteringDomainObjectDelegateProviderWithAction<out TaskContainer, Task>.provideDelegate(
receiver: Any?,
property: KProperty<*>
) = ExistingDomainObjectDelegate.of(
delegateProvider.register(property.name, action)
)
The provideDelegate operator allows for more complex logic in delegate creation. As per the docs:
By defining the provideDelegate operator you can extend the logic of creating the object to which the property implementation is delegated. If the object used on the right hand side of by defines provideDelegate as a member or extension function, that function will be called to create the property delegate instance.
Regarding the "passing a lambda to a property":
This is implemented via overloading of the invoke operator as an extension function on the TaskProvider class:
operator fun <T> NamedDomainObjectProvider<T>.invoke(action: T.() -> Unit) =
configure(action)
Basically, the call hello { /* your lambda */ } is desugared into hello.invoke { /* your lambda */ }.

Kotlin #FunctionalInterface compiles with multiple abstract methods

When trying to compile a Java #FunctionalInterface having more than 1 non-abstract method a compilation error is raised.
However, when doing the same in Kotlin, no errors or warnings are raised, i.e. the following Kotlin interface compiles successfully:
#FunctionalInterface
interface Foo {
fun foo()
fun foo(params: Map<String, String>)
}
Is this the intended behaviour or a bug in the Kotlin compiler?
Please note that the generated bytecode for the above Kotlin snippet is equivalent to the following Java snippet (which – correctly – doesn't compile):
#FunctionalInterface
// metadata omitted
public interface Foo {
void foo();
void foo(#NotNull Map var1);
}
Issue KT-25512 has been submitted to JetBrains's issue tracker (by another user) to report the fact that the compiler misbehaves when #FunctionalInterface is applied to a non-SAM interface, and as of 10 Feb 2019 the issue is still open with no activity.

Pass annotation to a function in Kotlin

How can I pass an annotion instance to a function?
I would like to call the java method AbstractCDI.select(Class<T> type, Annotation... qualifiers). But I don't know how to pass an annotation instance to this method.
Calling the constructor like
cdiInstance.select(MyClass::javaClass, MyAnnotation())
is not allowed and the #Annotation-Syntax cdiInstance.select(MyClass::javaClass, #MyAnnotation) is not allowed as parameter, too. How can I archive this?
When working with CDI you usually also have AnnotationLiteral available or at least you can implement something similar rather easy.
If you want to select a class using your annotation the following should do the trick:
cdiInstance.select(MyClass::class.java, object : AnnotationLiteral<MyAnnotation>() {})
Or you may need to implement your specific AnnotationLiteral-class if you require a specific value. In Java that would work as follows:
class MyAnnotationLiteral extends AnnotationLiteral<MyAnnotation> implements MyAnnotation {
private String value;
public MyAnnotationLiteral(String value) {
this.value = value;
}
#Override
public String[] value() {
return new String[] { value };
}
}
In Kotlin however, you can't implement the annotation and extend AnnotationLiteral or maybe I just did not see how (see also related question: Implement (/inherit/~extend) annotation in Kotlin).
If you rather want to continue using reflection to access the annotation then you should probably rather use the Kotlin reflection way instead:
ClassWithAnno::class.annotations
ClassWithAnno::methodWithAnno.annotations
Calling filter, etc. to get the Annotation you desire or if you know there is only one Annotation there, you can also just call the following (findAnnotation is an extension function on KAnnotatedElement):
ClassWithAnno::class.findAnnotation<MyAnnotation>()
ClassWithAnno::methodWithAnno.findAnnotation<MyAnnotation>()
One could annotate a method or field with the annotation an get it per Reflection:
this.javaClass.getMethod("annotatedMethod").getAnnotation(MyAnnotation::class.java)
Or According to Roland's suggestion the kotlin version of the above:
MyClass::annotatedMethod.findAnnotation<MyAnnotation>()!!
As suggested by Roland for CDI it is better to use AnnotationLiteral (see his post).

Kotlin default arguments in interface bug?

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.

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