4

Motivation:

In our Android project we have many verifications like str != null && !str.isEmpty(), so I decided to refactor them to a helper method. For a moment I use following class as a helper:

public class StringUtil {
    public static boolean isNullOrEmpty(@Nullable String str) {
        return str == null || str.isEmpty();
    }
}

Problem:

We already have a string's helper class, written in Kotlin (say, String.kt). So, this is not clear to have two helpers (one in Java and one in Kotlin).

What I tried:

Naive approach to copy-past isNullOrEmpty() inside String.kt do not successed, because $reciever is null, so it crashed.

Secondly, I tried to used Kotlin native isNullOrEmpty() from kotlin.text (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/is-null-or-empty.html):

public inline fun CharSequence?.isNullOrEmpty(): Boolean

but I cannot figure out how to call it from Java. This page (https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html) do not provide any suggestions.

The problem is not about Accessing Kotlin extension functions from Java. My extension is perfectly visibly, but it crash because of null-receivier. As I mentioned below, question is more about accessing native library code, not my own extension.

Any help please ?

Anton Malmygin
  • 3,406
  • 2
  • 25
  • 31
  • 1
    Possible duplicate of [Accessing Kotlin extension functions from Java](https://stackoverflow.com/questions/28294509/accessing-kotlin-extension-functions-from-java) – Salem Feb 28 '18 at 09:49
  • @Moira my question is about accessing native library code, not my own extension, please read carefully question before marking it as duplicate. – Anton Malmygin Feb 28 '18 at 10:03
  • Could you please clarify what exactly you mean by "$receiver is null, so it crashed"? If you declare an extension function for a nullable receiver type (`String?.isNullOrEmpty`), you can pass null as the $receiver argument to them. – yole Feb 28 '18 at 10:09
  • Well, that's not native code, it is the standard library, and it is about extension functions (it is irrelevant whether that is _your_ code or not). For most purposes, this _is a duplicate_. yole points out that some stdlib functions are inaccessible from Java due to `@InlineOnly` which marks the compiled function as `synthetic`. – Salem Feb 28 '18 at 10:09
  • if you edit your question avoid stuff like "edit" "update" etc. Phrase it so that it still reads as an original question. Also refrain from saying "plz no duplicate" etc – Tim Feb 28 '18 at 10:10
  • @Moira there is a difference - I cannot modify code from standard library, so most of suggestions from "duplicate" answer is irrelevant. – Anton Malmygin Feb 28 '18 at 10:13
  • That is false, none of the answers from that question recommend modifying the extension function. – Salem Feb 28 '18 at 10:14
  • You are wrong, they suggest to put @JvmStatic or add companion to an extension. And the rest is about Java way of generation code from Kotlin, which, again, is not a point of my question. – Anton Malmygin Feb 28 '18 at 10:17
  • Your question quite clearly states "I cannot figure out how to call it from Java", which seems to be the point of your question, and the accepted answer there states that top-level functions are compiled into `[Filename]Kt.[method name]`. The only answer suggesting `@JvmStatic` is the one with the least votes, and also arguably the worst solution. – Salem Feb 28 '18 at 10:20
  • Again, this is about STANDARD library function, not my own extension. I already understand subtlety about `@InlineOnly` and the accepted answer from another question did not helped me at all. – Anton Malmygin Feb 28 '18 at 10:29
  • 2
    or you can use this: https://developer.android.com/reference/android/text/TextUtils.html#isEmpty(java.lang.CharSequence) – Sarthak Mittal Feb 28 '18 at 11:14

2 Answers2

5

Some standard library functions, including this one, are marked with the @InlineOnly annotation, which makes them inaccessible from Java. For most other functions, you can access them exactly as described in the question linked as a duplicate.

yole
  • 92,896
  • 20
  • 260
  • 197
  • Thank you for explanation! Btw I do not succeed to move it to extension, because crash. I'll try to use your suggestion about `String?`. – Anton Malmygin Feb 28 '18 at 10:14
2

After suggestion from @yole, I finally manage to convert code correctly to extension:

fun String?.isNullOrEmpty(): Boolean = (this == null || this.isEmpty())

So, problem was in defining extension as String, not as String? which leads to crash on $receiver. (Kotlin generates hidden not-null verifications when translating to Java, see https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#null-safety)

Alternatively, standard library functions marked with @InlineOnly can be accessed from Java via custom extension.

fun String?.isVoid(): Boolean = this.isNullOrEmpty()

My problem here was in misunderstanding of ?. I tried this first:

fun String?.isVoid(): Boolean? = this?.isNullOrEmpty()

As @Moria mentioned, please note behaviour of ? - b?.method will return b.method if b is not null, and null otherwise (https://kotlinlang.org/docs/reference/null-safety.html#safe-calls)

Anton Malmygin
  • 3,406
  • 2
  • 25
  • 31
  • [`isNullOrEmpty` definitely does not return a `Boolean?`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/is-null-or-empty.html), what do you mean? – Salem Feb 28 '18 at 12:21
  • @Moira When I tried to use it as `fun String?.isVoid(): Boolean = this?.isNullOrEmpty()`, I cannot compile with error `Type mismatch: inferred type is Boolean? but Boolean was expected` – Anton Malmygin Feb 28 '18 at 12:41
  • 1
    You don't use it with `?.`, you use it as `this.isNullOrEmpty()` because the function can work with nullable types. `?.` forces the function to work with the non-null value, or return null if it is null (which is not what you want) – Salem Feb 28 '18 at 13:43