Accessing a Kotlin extension function from Java - kotlin

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.

Related

unmapped target property and mapstruct calls constructor with null parameter

I'm exploring mapstruct to map JPA entities and DTO objects. Entities and DTOs have abstract base classes that contain id and version fields that I'd like to keep private so that they can not be modified (public getter, no setter for both types). I made a most simple reproducer to demonstrate the idea. Abstract Base class has a private field name. To copy the field values back and forth Base defines a constructor that has a Base parameter. The constructor picks the private field from the parameter and assigns it to it's own private field:
package de.ruu.lab.map.read_only_field_in_base;
public abstract class Base
{
private String name;
public Base(String name) { this.name = name; }
protected Base(Base source) { name = source.name; }
public String getName() { return name; }
}
These are the subclasses of Base:
package de.ruu.lab.map.read_only_field_in_base;
import de.ruu.lab.map.read_only_field_in_base.SimpleMapper.Default;
public class Source extends Base
{
public Source(String name) { super(name); }
#Default
public Source(Base base) { super(base); }
}
package de.ruu.lab.map.read_only_field_in_base;
import de.ruu.lab.map.read_only_field_in_base.SimpleMapper.Default;
public class Target extends Base
{
public Target(String name) { super(name); }
#Default
public Target(Base base) { super(base); }
}
I have to annotate the default constructor to resolve constructor ambiguity for mapstruct. The mapper looks like this:
package de.ruu.lab.map.read_only_field_in_base;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import org.mapstruct.Mapper;
import org.mapstruct.Qualifier;
import org.mapstruct.factory.Mappers;
#Mapper
public interface SimpleMapper
{
SimpleMapper INSTANCE = Mappers.getMapper(SimpleMapper.class);
Source toSource(Target target);
Target toTarget(Source source);
#Qualifier // make sure that this is the MapStruct qualifier annotation
#java.lang.annotation.Target(CONSTRUCTOR)
#Retention(CLASS)
public #interface Default { }
}
The first problem is that mapstruct warns that there is an unmapped target property "base". What does that mean? Which target property is not mapped? Wouldn't it be possible to print the name of the property in the warning? I use eclipse as IDE, maybe the behaviour is different with other tools?
I tried annotating the mapping methods with
#Mapping(target="name", ignore = true)
but that does not let the warning disappear.
Because mapstruct just makes a warning I hoped everything would be ok and I created a tiny test class:
package de.ruu.lab.map.read_only_field_in_base;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
class SimpleMapperTest
{
#Test void shouldMapSourceToTarget()
{
Source source = new Source("map me");
Target target = SimpleMapper.INSTANCE.toTarget(source);
assertThat(target.getName(), is(source.getName()));
}
#Test void shouldMapTargetToSource()
{
Target target = new Target("map me");
Source source = SimpleMapper.INSTANCE.toSource(target);
assertThat(source.getName(), is(target.getName()));
}
}
Both tests fail with a NPE because of some strange code mapstruct generated:
package de.ruu.lab.map.read_only_field_in_base;
import javax.annotation.processing.Generated;
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-09-18T11:08:20+0200",
comments = "version: 1.5.2.Final, compiler: Eclipse JDT (IDE) 1.4.200.v20220802-0458, environment: Java 17.0.2 (GraalVM Community)"
)
public class SimpleMapperImpl implements SimpleMapper {
#Override
public Source toSource(Target target) {
if ( target == null ) {
return null;
}
Base base = null;
Source source = new Source( base );
return source;
}
#Override
public Target toTarget(Source source) {
if ( source == null ) {
return null;
}
Base base = null;
Target target = new Target( base );
return target;
}
}
Obviously code like this causes the NPE:
Base base = null;
Source source = new Source( base );
IMO this would be correct ("target" is the name of the method's parameter):
Source source = new Source( target );
Maybe this can be solved in an upcoming version. Meanwhile, is there any recommendation how to deal with this now?
Thanks!
The reason why you are getting the warnings is the fact that MapStruct doesn't really care about the private / protected fields you have.
When performing a mapping MapStruct looks at the setters and the constructor parameters to decide which properties need to be mapped.
Looking at your examples you have annotated the constructor that takes Base as a default constructor. This means that from the point of view of MapStruct your objects have properties that are taking Base and thus there is the warning for the unmapped property base.
There are 2 ways that you can do to fix this:
Instruct MapStruct how to map to the base property
#Mapper
public interface SimpleMapper
{
SimpleMapper INSTANCE = Mappers.getMapper(SimpleMapper.class);
#Mapping(target = "base", source = "target")
Source toSource(Target target);
#Mapping(target = "base", source = "source")
Target toTarget(Source source);
}
using the #Mapping you will tell MapStruct that you want to map the source parameters to the base property.
Annotated the constructor with the string a #Default
Instead of annotating the constructor that takes Base as input you can annotate the constructor that takes String as a default constructor.
This way MapStruct will look for how to map a property with the name name and thus will use the public Base#getName method to perform the mapping.
Note: I saw that you have #Qualifier on the #Default annotation, this is not needed. The only requirement for MapStruct for the default annotation is that it needs to be named like that.

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.

kotlin: how can I call function from object expression

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

Kotlin type inference failed

Let's say we have a 3rd-party Java library with such class:
//----------------------------------------------------------------------------------------
package foo;
public final class Functions {
public interface VoidFunc0 {
void call();
}
public interface VoidFunc1<T> {
void call(T t);
}
#SuppressWarnings("unchecked")
public static <T> NoOpFunc<T> noOp() {
return new NoOpFunc();
}
/*public*/ static final class NoOpFunc<T> implements VoidFunc0, VoidFunc1<T> {
#Override public void call() { /* deliberately no op */}
#Override public void call(T t) { /* deliberately no op */ }
}
}
//----------------------------------------------------------------------------------------
We successfully used its Functions.noOp() method in our Java application, but when we began to rewrite it in Kotlin, we faced the issue that the code below doesn't compile and gives us two errors:
//----------------------------------------------------------------------------------------
package bar
import foo.Functions
object KotlinApp {
#JvmStatic
fun main(args: Array<String>) {
/*
* Error:(XX, XX) Kotlin: Type inference failed: Not enough information
* to infer parameter T in fun <T : Any!> noOp(): Functions.NoOpFunc<T!>!
* Please specify it explicitly.
*/
callVoidFunc0(Functions.noOp()) // ERROR 1
/*
* Error:(XX, XX) Kotlin: Type Functions.NoOpFunc<Any!>! is inaccessible
* in this context due to: Functions.NoOpFunc<Any!>!
*/
callVoidFunc1(Functions.noOp()) // ERROR 2
}
fun callVoidFunc0(func0: Functions.VoidFunc0) {
func0.call()
}
fun callVoidFunc1(func1: Functions.VoidFunc1<Any>) {
func1.call("A")
}
}
//----------------------------------------------------------------------------------------
but the same code previously written in Java compiles and works well:
//----------------------------------------------------------------------------------------
package bar;
import foo.Functions;
public class JavaApp {
public static void main(String[] args) {
callVoidFunc0(Functions.noOp()); // OK
callVoidFunc1(Functions.noOp()); // OK
}
public static void callVoidFunc0(Functions.VoidFunc0 func0) {
func0.call();
}
public static void callVoidFunc1(Functions.VoidFunc1<Object> func1) {
func1.call("A");
}
}
//----------------------------------------------------------------------------------------
Type inference fails even if we specify T explicitly. Error 2 goes away when NoOpFunc declared as public, but Error 1 still remains.
The problem is a bug in Kotlin.
Here's the link to the issue: https://youtrack.jetbrains.com/issue/KT-14499. Please vote.
UPD
To fix the issue there's a workaround:
#JvmStatic
fun main(args: Array<String>) {
#Suppress("INACCESSIBLE_TYPE")
callVoidFunc0(Functions.noOp()) // (1)
#Suppress("INACCESSIBLE_TYPE")
callVoidFunc1(Functions.noOp<Any>()) // (2)
}
To fix (1) one must suppress the compilation warning, to fix (2) - additionally specify the type explicitly.

How to access static fields with reflect in kotlin?

I have this abstract class in java:
abstract class AbsApiTestCase<T> {
T mApi;
#Before
public void setUp() throws Exception {
mApi = instanceApi((Class<T>) (
(ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]);
}
static <T> T instanceApi(Class<T> clazz) throws Exception {
return new Retrofit.Builder()
.baseUrl(clazz.getField("BASE_URL").get(null).toString())
.addConverterFactory(GsonConverterFactory.create(
new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getClient())
.build().create(clazz);
}
// some code
}
And api looks like this:
public interface GithubApi {
String BASE_URL = "https://api.github.com/";
// some code
}
It can be used like this:
public class GithubApiTest extends AbsApiTestCase<GithubApi> {
// some code
}
But when I convert my code to kotlin, the static field BASE_URL looks like this:
interface GithubApi {
companion object {
val BASE_URL = "https://api.github.com/"
}
// some code
}
And BASE_URL cannot be accessed like above. I found there is a #JvmField annotation but Android studio says JvmField cannot be applied to a property defined in companion object of interface.
Is there a way to access this "static field"?
How about making BASE_URL a compile-time constant?
interface GithubApi {
companion object {
const val BASE_URL = "https://api.github.com/"
}
}
At byte-code level BASE_URL is a static field of the GithubApi interface.
public interface GithubApi {
public static final GithubApi$Companion Companion;
public static final java.lang.String BASE_URL;
static {};
Code:
0: new #26 // class GithubApi$Companion
3: dup
4: aconst_null
5: invokespecial #30 // Method GithubApi$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #32 // Field Companion:LGithubApi$Companion;
11: return
}
The #JvmStatic annotation will make the property's backing field a static one. That is, if the annotation is applied to a property within a companion object, then a new static field will be created in the enclosing class.
Note that Kotlin really has no notion of static, and that this annotation is merely for accessibility accross JVM languages.
I see four basic options for that:
1) Extract the property into an object (companion or not):
object GithubApiUrls {
val BASE_URL = "https://api.github.com/"
}
2) Make a package-level property:
package myProgram
val BASE_URL = "https://api.github.com/"
3) Use a class, not an interface, if you need inheritance (no idea what for, TBH):
open class GithubApi {
val BASE_URL = "https://api.github.com/"
}
4) Replace the field with a method in the interface (can be overridden):
interface GithubApi {
fun BaseUrl() = "https://api.github.com/"
}
Wouldn't suggest putting constants in interfaces at all. It introduces too much complexity without real value gain.
Try decoupling class that holds the constant from the actual class that implements the interface.
public class AllUrls {
public static final String GITHUB_URL = "https://api.github.com/";
}
That will become
object AllUrls {
val GITHUB_URL = "https://api.github.com/"
}
And to use it
static <T> T instanceApi(Class<T> clazz) throws Exception {
return new Retrofit.Builder()
.baseUrl(AllUrls.INSTANCE.getGITHUB_URL())
.addConverterFactory(GsonConverterFactory.create(
new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getClient())
.build().create(clazz);
}