Accessing Kotlin extension functions from Java - 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?

All Kotlin functions declared in a file will be compiled by default to static methods in a class within the same package and with a name derived from the Kotlin source file (First letter capitalized and ".kt" extension replaced with the "Kt" suffix). Methods generated for extension functions will have an additional first parameter with the extension function receiver type.
Applying it to the original question, Java compiler will see Kotlin source file with the name example.kt
package com.test.extensions
public fun MyModel.bar(): Int { /* actual code */ }
as if the following Java class was declared
package com.test.extensions
class ExampleKt {
public static int bar(MyModel receiver) { /* actual code */ }
}
As nothing happens with the extended class from the Java point of view, you can't just use dot-syntax to access such methods. But they are still callable as normal Java static methods:
import com.test.extensions.ExampleKt;
MyModel model = new MyModel();
ExampleKt.bar(model);
Static import can be used for ExampleKt class:
import static com.test.extensions.ExampleKt.*;
MyModel model = new MyModel();
bar(model);

Kotlin top-level extension function are compiled as Java static methods.
Given Kotlin file Extensions.kt in package foo.bar containing:
fun String.bar(): Int {
...
}
The equivalent Java code would be:
package foo.bar;
class ExtensionsKt {
public static int bar(String receiver) {
...
}
}
Unless, that is, Extensions.kt contained the line
#file:JvmName("DemoUtils")
In which case the Java static class would be named DemoUtils
In Kotlin, extension methods can be declared in other ways. (For example, as a member function or as an extension of a companion object.)

With newer KotlinEx, you can directly call extension in java
ExtensionFileName.foo(field1...)
Basically, what it does is, it makes the receiver as first arguement and other arguments remain being at the same place
So For eg.
You have extension(in file Extension.kt)
Context.showToast(message:String){
...
}
In Java, you call it as
ExtensionKt.showToast(context, message);

I have a Kotlin file called NumberFormatting.kt that has the following function
fun Double.formattedFuelAmountString(): String? {
val format = NumberFormat.getNumberInstance()
format.minimumFractionDigits = 2
format.maximumFractionDigits = 2
val string = format.format(this)
return string
}
In java I simple access it over the file NumberFormattingKt in the following way after the required import import ....extensions.NumberFormattingKt;
String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());

You can always see the actual Java code which is getting generated from your Kotlin code by going to Tools > Kotlin > Show Kotlin Bytecode, then clicking Decompile. This can help you tremendously. In your case the Java code will look like this if you have MyModelExtensions.kt
public final class MyModelExtensionsKt {
public static final int bar(#NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
you can improve over this by using #JvmName on the file containing bar:
#file:JvmName("MyModels")
package io.sspinc.datahub.transformation
public fun MyModel.bar(): Int {
return this.name.length
}
and it will result in this code:
public final class MyModels {
public static final int bar(#NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
Using MyModels is in line with what Effective Java suggests for utility classes. You can also rename your method like this:
public fun MyModel.extractBar(): Int {
return this.name.length
}
then from the Java side it will look idiomatic:
MyModels.extractBar(model);

It works for me:
Kotlin
Java code
My project is an old android project created with Java; now I created the first kotlin file and added String extensions
fun String.isNotNullOrEmpty(): Boolean {... }
and I could call it from java file using:
StringUtilsKt.isNotNullOrEmpty(thestring).
My kotlin file name is StringUtils

The other answers here cover the case of calling an extension function located at the top level of a Kotlin package file.
However, my case was that I needed to call an Extension function located inside a Class. Specifically, I was dealing with an Object.
The solution is incredibly simple.
All you have to do is annotate your extension function as #JvmStatic, and voila! Your Java code will be able to access it and use it.

When you extend a class like this:
fun String.concatenatedLength(str: String): Int {
return (this.length + str.length)
}
fun f() {
var len = "one string".concatenatedLength("another string")
println(len)
}
It will compile to this:
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
public final class ExampleKt {
public static final int concatenatedLength(#NotNull String $receiver, #NotNull String str) {
Intrinsics.checkParameterIsNotNull((Object) $receiver, (String) "$receiver");
Intrinsics.checkParameterIsNotNull((Object) str, (String) "str");
return $receiver.length() + str.length();
}
public static final void f() {
int len = ExampleKt.concatenatedLength("one string", "another string");
System.out.println(len);
}
}
There are more examples here.

As far as I can tell this isn't possible. From my reading of the extensions docs, it appears that
public fun MyModel.bar(): Int {
return this.name.length()
}
creates a new method with the signature
public static int MyModelBar(MyModel obj) {
return obj.name.length();
}
Then, Kotlin maps that function to calls of the form myModel.bar(), where if bar() isn't found in the MyModel class it looks for static methods matching the signature and naming scheme it outputs. Note that this is just an assumption from their statements about extensions being statically imported and not overriding defined methods. I haven't gotten far enough in their source to know for sure.
So, assuming the above is true there's no way for Kotlin extensions to be called from plain old java code, as the compiler will just see an unknown method being called on an object and error out.

Related

Using a java class inside another class without it's java file being actually present

I am writing a backend where the users can write some code inside a pre-written class like so,
import java.utils.*;
public class SomeClass{
public int fun(){
// some code written by the user.
}
}
Note : This SomeClass has no .java file in my project and loaded using the String code. The value of string being the above code itself.
I am creating this class object using GroovyClassLoader like so,
try (GroovyClassLoader gcl = new GroovyClassLoader()) {
// code is a String. The value of this code string is the above code.
Class<?> loadedClass = gcl.parseClass(code);
Class<?> add = gcl.parseClass(addClass);
// using this object
}
Now if I have another class or classes like so,
import java.utils.*;
public class Addition{
public int add(int a, int b){
return a+b;
}
}
Note:This is also not present as a .java file in my code base, it is present as a String variable as above.
I can use the GroovyClassLoader to load both the class but how can is use this Addition class inside my SomeClass class, like this,
import java.utils.*;
// somehow importing Addition class
public class SomeClass{
public int fun(){
Addition addition = new Addition();
return addition.add(2, 3);
}
}
As clearly mentioned above none of these files are actually present as .java files in my project and thus there is no package for any of these classes, so is there anyway in which SomeClass can import and use Addition. If not then is there anyway this can be achieved.
Note : Another example of my problem can be some online coding sites like Leetcode for example, there we only write the main code (in my case SomeClass) and the boilerplate code (like initialising the arrays, variable and taking input and output, in my case the Addition class) is injected to my main code by LeetCode itself. So I want to achieve something like this.

How does function that isn't part of a class can be called from a class's function?

I'm new in Kotlin (have some experience with Java). In java, each function (method) belong to class (static function or instance function). In Java It isn't possible to write a function outside a class .In kotlin i see that it is possible. I can't understand how, calling a function (that isn't part of a class scope) from a class function works.
Can someone explain me.
Code Example:
Class Wolf:
class Wolf()
{
var image = "wolf.jpg"
var food = "meat"
val habitat = "forests"
fun makeNoise() {
println("Hooooow!")
addNumbers(5, 6)
}
}
Method that isn't part of class:
fun addNumbers (a:Int, b:Int): Int
{
return a+b
}
From the kotlin docs:
In Kotlin functions can be declared at top level in a file, meaning you do not need to create a class to hold a function, which you are required to do in languages such as Java, C# or Scala. In addition to top level functions, Kotlin functions can also be declared local, as member functions and extension functions.
If you are wanting to know how this is implemented in JVM bytecode you can use the "Tools -> Kotlin -> Show Kotlin Bytecode" then "Decompile" feature in IntelliJ.
You will see that if you put addNumbers in a Wolf.kt file, then the kotlin compiler generates the bytecode equivalent of:
public final class WolfKt {
public static final int addNumbers(int a, int b) {
return a + b;
}
}
You can control the name of the class with #file:JvmName("WolfFunctions")

Kotlin top level function with private java contructor

I have the kotlin file with top level function and specified name for using it from java code:
Utility.kt
#file:JvmName("Utility")
package com.example
fun someUtilityFunc() {
// ignored
}
and I can instantiate Utility class in my Java code:
Main.java
package com.example;
public Main {
public static void main(String[] args) {
new Utility();
}
}
How can I add the private constructor to generated Utility class?
You can't. file:JvmName will generate a regular class with static methods.
If you absolutely don't want to have this constructor, you'll have to use object
object Utility {
fun someUtilityFunc() {
// ignored
}
}
Now previously, you could just called someUtilityFunc() in your Kotlin code, and it just worked.
But thankfully you can import functions in Kotlin, so it still works without prefixing:
import com.example.Utility.someUtilityFunc
fun main() {
someUtilityFunc()
}

How to use Scala implicit class in Java

I have a Scala Implicit class from RecordService API, which i wanted to use in Java file.
package object spark {
implicit class RecordServiceContext(ctx: SparkContext) {
def recordServiceTextFile(path: String) : RDD[String] = {
new RecordServiceRDD(ctx).setPath(path)
.map(v => v(0).asInstanceOf[Text].toString)
}
}
}
Now i am trying to import this in a Java file using below import.
import com.cloudera.recordservice.spark.*;
But i am not able to use recordServiceTextFile("path") from sparkContext.
In Scala the import is little different and its working.
Here is simple definition of implicit class in package object
package object spark {
implicit class Ext(param: Int) {
def a = param + 1
}
}
and here is how you can use it from java
public class Test {
public static void main(String[] args) {
spark.package$.MODULE$.Ext(123).a();
}
}
so you can basically use RecordServiceContext as a method that wraps your SparkContext and adds an extra method that you can call. That is optimization for implicit classes.
That would be something like this:
SparkContext c = ???
RDD<String> rdd = com.cloudera.recordservice.spark.package$.MODULE$.RecordServiceContext(c)
.recordServiceTextFile("asdf");
A package object spark is compiled to a class package in the package spark. The implicit class RecordServiceContext will get compiled to a static method RecordServiceContext (that's scala's implicit def) in package and a class package$RecordServiceContext.
So the following code should do it:
import com.cloudera.recordservice.spark.*;
//some code
RDD<String> rdd = package.RecordServiceContext(myContext).recordServiceTextFile(pathToFile);
//some code
But package is probably a reserved keyword, and Java has no way of escaping them as far as I know. So you'll have to do some reflection to invoke the RecordServiceContext method.
SparkContext ctx = ...
RecordServiceContext rsct = new RecordServiceContext(ctx)
recordServiceTextFile("/your_path")
This should do it.
String s = new spark.RecordServiceContext("safa").recordServiceTextFile("dsf");
I changed the signatures though.
My Scala class looks like this ,
object spark {
implicit class RecordServiceContext(ctx: String) {
def recordServiceTextFile(path: String) : String = {
"test"
}
}
}
My java class looks like this,
public class TestScalaCall {
public static void main(String args[]){
String s = new spark.RecordServiceContext("safa").recordServiceTextFile("dsf");
}
}
Edit ---
So a quick look of the Scala change requests shows us this.
They are actually working on making a class defined under a package object behave the same way as defining it inside a regular package. But that is targeted for the yet to be release 2.12 .
So the recommendation they are giving is keep only absolutely necessary classes/objects that do not needs any external interaction inside package objects. Otherwise keep them under regular packages.
So for now you need to not use the package object construct.
Also , a point worth pondering "Does it really make sense to define something that is accessible on the outside inside a package object ? "

Is it possible to change buggy class in a closed source library that was not built to support dependency injection?

Say I am using a closed source java library with a known buggy class, say BuggyClass and this class is hardcoded throughout the rest of the library. So I would imagine that the java library looks something like this:
public class BuggyClass {
public T buggyMethod (...) {
// Buggy code here
}
}
with several other classes in the library that make use of this class:
public class Example {
private BuggyClass = new BuggyClass(); // No dependency injection possible
public Example (/* No way to pass in my own subclass of BuggyClass*/) {
// ...
}
}
etc...
Is there any hack, or workaround, possibly using the class loader so that I could subclass BuggyClass and get Example (and every other class in the library that has BuggyClass hardcoded in) to use my subclass?
You can't do a subclass, no, but you can write your own BuggyClass entirely and make sure it appears earlier in the classpath than the real one. I don't think it's documented, but the default classloader seems to typically use the first matching class it finds.
But obviously this is a Really Bad Option, so you want to exhaust every other avenue before trying to solve temporarily work around the underlying problem this way.
Example: Suppose we have this:
// The "buggy" class
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the buggy class";
}
}
and this:
// Uses the "buggy" class
package somepackage;
public class BuggyClassUser {
public String useBuggyClass() {
BuggyClass c = new BuggyClass();
return c.someMethod();
}
}
compiled and the classes in buggy.jar. Then we have this test class:
import somepackage.*;
public class Test {
public static final void main(String[] args) {
BuggyClassUser u = new BuggyClassUser();
System.out.println(u.useBuggyClass());
}
}
If we run that (*nix format classpath):
java -cp .:buggy.jar Test
...we see
I'm in the buggy class
But if we create a somepackage directory and put this in it:
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the fixed class"; // <== Difference here
}
}
...and compile that, since we have that in our classpath in front of the jar, this command:
java -cp .:buggy.jar Test
...now gives us this:
I'm in the fixed class
Again, this is very, very much a workaround, not a solution.

Categories