Iain Merrick
Iain Merrick

Reputation: 403

Can I mark a method as deprecated for Kotlin callers, but not for Java callers?

I want to extend an API in a way that's useful for Kotlin callers, but not Java callers. Therefore I'd like to encourage Kotlin users to switch to a new method, without bothering Java users. We have a large codebase that's a mix of Java and Kotlin, and won't be 100% Kotlin any time soon.

Is there a way to add a @Deprecate tag or similar that only shows up in Kotlin, not Java?

If you're interested in the specific details, we have a Log interface that takes a formatted string, something like this:

Log.get().info("Log message with some $expensive $things to $format")

That string is created on every call, even if logging is disabled. Fixing the logger API to take the format string and arguments separately would be a big and disruptive change. However, Kotlin's nullable types could give us a very cheap solution:

Log.getNullable()?.info("This $expensive $format should be skipped if there's no logger")

Just a case of replacing get(). with getNullable()?. in all the Kotlin code. But I wouldn't want to use getNullable() in Java code because there's a risk of NullPointerException. So the old Log.get method should ideally be deprecated in Kotlin, but not in Java.

Upvotes: 2

Views: 1309

Answers (2)

Vishal Ambre
Vishal Ambre

Reputation: 463

There are two parts for this

  • Hide a method from Kotlin Caller
  • Hide a method from Java Caller

For hiding a method from Java Caller, @JvmSynthetic can be used; more on it here. For hiding a method from Kotlin Caller I could not find a direct way of doing it but as suggested here you can use @JvmName to provide a good name for the method and add a deprecation message in the actual fun name

So now the Class will look like

class Log {
    companion object {
        @JvmStatic
        @JvmName("get")
        fun dont_dare_using_this_from_kotlin_use_getNullable_instead(): String = TODO()

        @JvmSynthetic
        fun getNullable(): String = TODO()
    }
}

Upvotes: 1

Roland
Roland

Reputation: 23292

Assuming a class Log whose methods are defined in the companion object, e.g.:

class Log {
  companion object {
    @JvmStatic
    fun get() : String = TODO()
    @JvmStatic
    fun getNullable() : String? = TODO()
  }
}

What about just removing the @JvmStatic? or creating an extension function instead?

fun Log.Companion.getNullable() : String? = TODO()
class Log {
  companion object {
    @JvmStatic
    fun get() : String = TODO()
  }
}

Extension functions are usually not that easily accessible in Java as they are in Kotlin. Both, removing @JvmStatic or the extension function would require the following in the Java code: Log.Companion.getNullable instead of Log.getNullable... Maybe that's also ok for you instead of deprecating it.

Even if my guess is off, an extension function may suit your needs here.

Regarding deprecating a method only for Kotlin... I don't think that works, but here is another workaround for that. Just rename the method and use @JvmName("get") on the method, e.g.:

@JvmStatic
@JvmName("get")
fun dont_ever_use_this_method_again_in_Kotlin() : String = TODO()

That will allow Log.get from Java but doesn't from Kotlin. And I think we can agree that no one will call Log.dont_ever_use_this_method_again_in_Kotlin in Kotlin, right?

Upvotes: 1

Related Questions