3

I'm currently writing a KOTLIN class were I like to use the possibility of DSL but be backwards compatible for JAVA developers. When using a var the compiler automatically creates getter and setter for JAVA, but those can't be used Builder style since they are not returning this.

So basically what I like to do is something like this:

class MyClass {
    // for use in KOTLIN only DSL style e.g. MyClass() { offset = 1 }
    var offset: Int? = null

    // for use in JAVA only BUILDER style e.g. new MyClass().withOffset(1)
    fun withOffset(value: Int) = apply { offset = value }
}

In Kotlin I like to use, but don't want to have access to the withOffset fun:

val myClass = MyClass() { offset = 1 }

In JAVA I like to use, but don't want to have access to the auto created setOffset and getOffset:

MyClass myClass = new MyClass().withOffset(1)

Renaming the getter and setter is possible via @JvmName annotation already, but is there a annotation for hiding a public property for JAVA completly and optional of course vice versa?

tekgator
  • 65
  • 5

2 Answers2

2

You cannot hide a constructor, while you can make a so called fake-constructor using operator fun invoke in which you can use a @JvmSyntheic annotation to hide it from the java.

And to hide a function from kotlin you can use @Deprecated with DeprecationLevel.HIDDEN.

Note: The @JvmField will instruct the compiler not to generate the default getter and setter for your var

class MyClass {
    @JvmField
    var offset: Int? = null

    @kotlin.Deprecated(message = "JUST FOR JAVA", level = DeprecationLevel.HIDDEN)
    fun withOffset(value: Int) = apply { offset = value }

    companion object {
        @JvmSynthetic
        operator fun invoke(init: MyClass.() -> Unit) = MyClass().apply(init)
    }
}

Usage in kotlin:

MyClass() { offset = 1 }
// or
MyClass { offset = 1 }

Usage in java:

MyClass().withOffset(1)

Resources:

Animesh Sahu
  • 7,445
  • 2
  • 21
  • 49
  • Thanks you for the quick answer. The deprecated method doesn't seem to work anymore. This is also mentioned in the reference you provided. There is also a trick mentioned which does work `@kotlin.SinceKotlin(version = "99999.0")` ... Not sure if I should use it. Hiding from Kotlin is not so important anyways. I will now try `@JvmSynthetic`. Will let you know if it works. – tekgator Jun 19 '20 at 19:42
  • @tekgator Deprecated still works, testing with Kotlin1.4-M1. See it shows Unresolved reference in playground as well 1.3.72 https://pl.kotl.in/Qjc5_WM4i – Animesh Sahu Jun 20 '20 at 02:13
  • Sorry maybe misleading comment. Indeed it hides it in Kotlin, but in Java as well. Error = Method Call expected, just like the method doesn't exist at all – tekgator Jun 20 '20 at 11:46
1

It's not possible to do something like this in Kotlin

val myClass = MyClass() { offset = 1 }

but I would suggest you do do this instead, it looks much nicer in my opinion.

// It's a inline function so there's no runtime overhead.
inline fun buildMyClass(builder: MyClass.() -> Unit): MyClass {
    return MyClass().apply(builder)
}

class MyClass {

    @JvmSynthetic
    var offset: Int? = null
        private set

    fun withOffset(value: Int) = apply { offset = value }
}

So you can call it like below

val myClass = buildMyClass {
   withOffset(0)
}

In Java, it looks like this:

final MyClass myClass = new MyClass().withOffset(0);
Seanghay
  • 1,076
  • 1
  • 11
  • 24