kotlin: how can I call function from object expression - kotlin

I have such code:
// foo.kt
fun bar() {
val foo = object {
fun zzz() {
println()
}
}
foo.zzz()
}
And I use kotlinc foo.kt to compile the kotlin code. I got 'FooKt.class' and 'FooKt$bar$foo$1.class' as result.
And then I decompile the 'FooKt.class' file to discover the java code, it is like this(by jd-gui):
import java.io.PrintStream;
import kotlin.Metadata;
#Metadata(mv={1, 1, 6}, bv={1, 0, 1}, k=2, d1={"\000\006\n\000\n\002\020\002\032\006\020\000\032\0020\001"}, d2={"bar", ""})
public final class MainKt
{
public static final void bar()
{
Object foo = new Object()
{
public final void zzz()
{
System.out.println();
}
};
foo.zzz(); // You can NOT do this in java world. But kotlin can do. Why?
}
}
I'm curious about the java code above. If you paste the code to any ide and compile(javac), there will be an error: zzz unresolved.
The error above say that you can't call any custom function from a new Object() even you extend it.
Now is the question: Kotlin generated such error code but all seems ok, why?

This is because Kotlin create an extra class extending from object FooKt$bar$foo$1.class. You can use anonymous objects always than you aren't exposing it outer the scope.
https://kotlinlang.org/docs/reference/object-declarations.html

Related

Kotlin: invoke function in companion object (via reflection)

I am trying to notify a central class on startup about the existence of some classes that will be used during runtime. My idea is to use reflection: scan for annotation (or interface implementation) and call a method from the companion's object of those classes.
As suggested in [1] I am using classgraph but I'm totally open to alternatives.
package com.test
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import io.github.classgraph.ScanResult
import kotlin.reflect.KFunction
import kotlin.reflect.jvm.kotlinFunction
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class OnStartup
// a class that will be instantiated a lot of times during runtime
data class SomeClass(val name: String) {
companion object {
#OnStartup
fun onStartup() {
// notify someone at startup about our existence
}
}
}
fun main() {
val scanResult: ScanResult = ClassGraph().enableAllInfo().acceptPackages("com.test").scan()
scanResult
.getClassesWithMethodAnnotation(OnStartup::class.java.name)
.filter { it.isStatic }
.flatMap { findStartupMethods(it) }
.forEach { it.call() }
}
private fun findStartupMethods(classInfo: ClassInfo): List<KFunction<*>> {
return classInfo.methodInfo.filter { function ->
function.hasAnnotation(OnStartup::class.java)
}.mapNotNull { method ->
method.loadClassAndGetMethod().kotlinFunction
}
}
The problem is, that the code exits with
Exception in thread "main" java.lang.IllegalArgumentException: Callable expects 1 arguments, but 0 were provided.
From reading the Kotlin Docs and [2] my guess is that I should hand over the companionObjectInstance as a parameter. But I have absolutely no idea how to get it...
Any help is really appreciated.
[1] Getting a list of annotated functions in Kotlin using reflection
[2] Kotlin invoke companion function with reflection
Maybe it looks ugly, but it works...
it.call((it.parameters[0].type.classifier as KClass<*>).objectInstance)

Kotlin NoArg plugin ignores declaration assignment

This problem is related to Kotlin noarg plugin not initializing default values.
I've got my NoArg plugin set up like this:
plugins {
id "org.jetbrains.kotlin.plugin.noarg" version "1.7.10"
}
noArg {
annotation("tools.AddEmptyConstructor")
}
Now, this kotlin code:
import tools.AddEmptyConstructor
#AddEmptyConstructor
class Test {
private constructor(test: String)
private val declarationAssignment = ArrayList<String>()
}
compiles to this java code:
import tools.AddEmptyConstructor;
public final class Test {
private final ArrayList declarationAssignment;
private Test(String test) {
this.declarationAssignment = new ArrayList();
}
public Test() {
}
}
I get that the empty constructor is supposed to be empty, but why is the declaration assignment, which isn't part of the constructor during compile time, not also being copied into the empty constructor?
How is that intended to be done? I don't have to use declaration assignments and I would be happy with workarounds, but I cannot access the empty constructor and thus not initialize values at all.

Accessing a Kotlin extension function from Java

Is it possible to access extension functions from Java code?
I defined the extension function in a Kotlin file.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Where MyModel is a (generated) java class. Now, I wanted to access it in my normal java code:
MyModel model = new MyModel();
model.bar();
However, that doesn't work. The IDE won't recognize the bar() method and compilation fails.
What does work is using with a static function from kotlin:
public fun bar(): Int {
return 2*2
}
by using import com.test.extensions.ExtensionsPackage so my IDE seems to be configured correctly.
I searched through the whole Java-interop file from the kotlin docs and also googled a lot, but I couldn't find it.
What am I doing wrong? Is this even possible?
Perhaps like this:
// CallExtensionFunction.java
package com.example.groundup;
public class CallExtensionFunction {
public static void main(String[] args) {
MyModel myModel = new MyModel();
int bar = MyModelKt.bar(myModel);
System.out.println(bar);
}
}
// MyModell.kt
package com.example.groundup
fun MyModel.bar(): Int {
return this.name.length
}
class MyModel() {
val name = "Hugo"
}
The extension function is provided in the corresponding singleton with the suffix "Kt" as a static method.

"Property must be initialized or be abstract" in init block when throwing an exception

Why does kotlin report Property must be initialized or be abstract. The object construction is never finished, so it should not matter whether a is initialized or not. Could a case be demonstrated where this would be a problem?
class Foo {
private val a: Int
init {
a = 42
throw Exception()
}
}
fun main() {
Foo()
}
kotlin playground
However these work just fine
fun bar() {
throw Exception()
}
class Foo {
private val a: Int
init {
a = 42
bar()
}
}
fun main() {
Foo()
}
kotlin playground
class Foo {
private val a: Int = throw Exception()
}
fun main() {
Foo()
}
kotlin playground
Similar java code works as expected:
public class Test {
private static class Foo {
private final int a;
public Foo() throws Exception {
a = 42;
throw new Exception();
}
}
public static void main(String []args) throws Exception {
new Foo();
}
}
The question is very well answered in the below link.
Kotlin: why do I need to initialize a var with custom getter?
Essentially it boils down to having a backing field for every "val" (property) . If you can provide a backing field, you need not initialize the field. Below is a small example of it.
class Foo {
private val a: Int
get() = getValue()
}
fun getValue():Int {
throw Exception()
}
fun main() {
Foo()
}
Similar java code works as expected:
Java initializes fields to 0 (or null/false depending on type) by default. You can see it e.g. by printing a's value before the a = 42 line.
Kotlin doesn't, because this implicit initialization makes it too easy to forget to initialize a property and doesn't provide much benefit. So it requires you to initialize all properties which have backing fields.
It seems to be a compiler bug as Alexey suggested
There is similar issue posted on Kotlin bug tracer.

Method References to Super Class Method

How to use method references to refer to super class methods?
In Java 8 you can do SubClass.super::method.
What would be the syntax in Kotlin?
Looking forward to your response!
Conclusion
Thanks to Bernard Rocha!
The syntax is SubClass::method.
But be careful. In my case the subclass was a generic class. Don't forget to declare it as those:
MySubMap<K, V>::method.
EDIT
It still doesn't work in Kotlin.
Hers's an example in Java 8 of a method reference to a super class method:
public abstract class SuperClass {
void method() {
System.out.println("superclass method()");
}
}
public class SubClass extends SuperClass {
#Override
void method() {
Runnable superMethodL = () -> super.method();
Runnable superMethodMR = SubClass.super::method;
}
}
I'm still not able to do the same in Kotlin...
EDIT
This is an example how I tried to achieve it in Kotlin:
open class Bar {
open fun getString(): String = "Hello"
}
class Foo : Bar() {
fun testFunction(action: () -> String): String = action()
override fun getString(): String {
//this will throw an StackOverflow error, since it will continuously call 'Foo.getString()'
return testFunction(this::getString)
}
}
I want to have something like that:
...
override fun getString(): String {
//this should call 'Bar.getString' only once. No StackOverflow error should happen.
return testFunction(super::getString)
}
...
Conclusion
It's not possible to do so in Kotlin yet.
I submitted a feature report. It can be found here: KT-21103 Method Reference to Super Class Method
As the documentation says you use it like in java:
If we need to use a member of a class, or an extension function, it
needs to be qualified. e.g. String::toCharArray gives us an extension
function for type String: String.() -> CharArray.
EDIT
I think you can achieve what you want doing something like this:
open class SuperClass {
companion object {
fun getMyString(): String {
return "Hello"
}
}
}
class SubClass : SuperClass() {
fun getMyAwesomeString(): String {
val reference = SuperClass.Companion
return testFunction(reference::getMyString)
}
private fun testFunction(s: KFunction0<String>): String {
return s.invoke()
}
}
Don't know if it is possible to get the reference to super class's function, but here is an alternative to what you want to achieve:
override fun getString(): String = testFunction { super.getString() }
According to Bernardo's answer, you might have something like this. It doesn't have remarkable changes.
fun methodInActivity() {
runOnUiThread(this::config)
}
fun config(){
}
What is more, in the incoming 1.2 version you can use just
::config