Deprecated ReplaceWith doesn't work with generics - kotlin

I got a function old(clazz: Class<T>), I want to deprecate and guide usage of it to new<T>() function.
fun <T> old(clazz: Class<T>): T {
TODO()
}
#Suppress("DEPRECATION")
inline fun <reified T> new(): T {
return old(T::class.java)
}
If I write
#Deprecated(message = "replace with new<T>()", replaceWith = ReplaceWith("new<T>()"))
fun <T> old(clazz: Class<T>): T {
TODO()
}
// auto correction of
val foo = old(String::class.java)
// becomes
val foo = new()
Auto correction of a code val foo = old(String::class.java) suggests "replace with 'new()' and ends up looking like this:
val foo = new(), which is incorrect, compilation error and miss generics.
But, if I change ReplaceWith pattern to "new<T remove>()" for example, which doesn't make sense, but auto correction correctly identifies generics and replaces it correctly and logically it also concats "remove" to it like this:
// ReplaceWith with "remove" at the end of generics
#Deprecated(message = "replace with new<T>()", replaceWith = ReplaceWith("new<T remove>()"))
fun <T> old(clazz: Class<T>): T {
TODO()
}
// usage of old
val foo = old(String::class.java)
// becomes (almost correctly)
val foo = new < String remove >()
Am I doing something wrong here ? How to define correct pattern for ReplaceWith which leverage inline reified generics in it ?

Related

Kotlin: generate a Factory by class

We're trying to do some generic processing in kotlin. Basically, for a given class, we want to get the related Builder object. i.a. for any object that extends a GenericObject, we want a Builder of that Object.
interface Builder<T : GenericObject>
object ConcreteBuilder: Builder<ConcreteObject>
We'd need a function that will return ConcreteBuilder from ConcreteObject
Our current implementation is a Map:
val map = mapOf<KClass<out GenericObject>, Builder<out GenericObject>>(
ConcreteObject::class to ConcreteBuilder
)
Then we can get it with:
inline fun <reified T : GenericObject> transform(...): T {
val builder = map[T::class] as Builder<T>
...
However this isn't very nice as:
we need an explicit cast to Builder<T>
the map has no notion of T, a key and a value could be related to different types.
Is there any better way to achieve it?
A wrapper for the map could be:
class BuilderMap {
private val map = mutableMapOf<KClass<out GenericObject>, Builder<out GenericObject>>()
fun <T: GenericObject> put(key: KClass<T>, value: Builder<T>) {
map[key] = value
}
operator fun <T: GenericObject> get(key: KClass<T>): Builder<T> {
return map[key] as Builder<T>
}
}
This hides the ugliness, while not completely removing it.
To use:
val builderMap = BuilderMap()
builderMap.put(ConcreteObject::class, ConcreteBuilder)
builderMap.put(BetonObject::class, BetonBuilder)
// builderMap.put(BetonObject::class, ConcreteBuilder) – will not compile
val builder = builderMap[T::class]

Create a var using a delegate that does not have a setter

I am trying to create delegate var properties with a delegate that does not provide a setValue(...) method. In other words, I need a property that I can reassign but that should get its value via the delegate as long as it hasn't been reassigned.
I am using the xenomachina CLI arguments parser library, which uses delegates. This works well as long as I have val properties. In some cases I need to be able to change those properties dynamically at runtime, though, requiring a mutable var. I can't simply use a var here, as the library does not provide a setValue(...) method in its delegate responsible for the argument parsing.
Ideally, I'd like something like this:
class Foo(parser: ArgParser) {
var myParameter by parser.flagging(
"--my-param",
help = "helptext"
)
}
which doesn't work due to the missing setter.
So far, I've tried extending the Delegate class with a setter extension function, but internally it also uses a val, so I can't change that. I've tried wrapping the delegate into another delegate but when I do that then the library doesn't recognize the options I've wrapped anymore. Although I may have missed something there.
I can't just re-assign the value to a new var as follows:
private val _myParameter by parser.flagging(...)
var myParameter = _myParameter
since that seems to confuse the parser and it stops evaluating the rest of the parameters as soon as the first delegate property is accessed. Besides, it is not particularly pretty.
How do you use delegates that don't include a setter in combination with a var property?
Here is how you can wrap a ReadOnlyProperty to make it work the way you want:
class MutableProperty<in R, T>(
// `(R, KProperty<*>) -> T` is accepted here instead of `ReadOnlyProperty<R, T>`,
// to enable wrapping of properties which are based on extension function and don't
// implement `ReadOnlyProperty<R, T>`
wrapped: (R, KProperty<*>) -> T
) : ReadWriteProperty<R, T> {
private var wrapped: ((R, KProperty<*>) -> T)? = wrapped // null when field is assigned
private var field: T? = null
#Suppress("UNCHECKED_CAST") // field is T if wrapped is null
override fun getValue(thisRef: R, property: KProperty<*>) =
if (wrapped == null) field as T
else wrapped!!(thisRef, property)
override fun setValue(thisRef: R, property: KProperty<*>, value: T) {
field = value
wrapped = null
}
}
fun <R, T> ReadOnlyProperty<R, T>.toMutableProperty() = MutableProperty(this::getValue)
fun <R, T> ((R, KProperty<*>) -> T).toMutableProperty() = MutableProperty(this)
Use case:
var lazyVar by lazy { 1 }::getValue.toMutableProperty()
And here is how you can wrap a property delegate provider:
class MutableProvider<in R, T>(
private val provider: (R, KProperty<*>) -> (R, KProperty<*>) -> T
) {
operator fun provideDelegate(thisRef: R, prop: KProperty<*>): MutableProperty<R, T> =
provider(thisRef, prop).toMutableProperty()
}
fun <T> ArgParser.Delegate<T>.toMutableProvider() = MutableProvider { thisRef: Any?, prop ->
provideDelegate(thisRef, prop)::getValue
}
Use case:
var flagging by parser.flagging(
"--my-param",
help = "helptext"
).toMutableProvider()
You could wrap your delegate with a class like this:
class DefaultDelegate<T>(private val default: Delegate<T>){
private var _value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? =
_value?: default.value
operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
_value = value
}
}
Usage:
class Foo(parser: ArgParser) {
var myParameter: Boolean? by DefaultDelegate(parser.flagging(
"--my-param",
help = "helptext"
))
}
If you need nullability:
class DefaultDelegate<T>(private val default: Delegate<T>){
private var modified = false
private var _value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? =
if (modified) _value else default.value
operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
_value = value
modified = true
}
}

Is it possible to make safe inline Optional in Kotlin?

In Kotlin sometimes I have to work with double nullability. For example, I need double nullability, when I want to use T? where T may be a nullable type. There are a few approaches for doing this:
Holder<T>? where Holder is data class Holder<out T>(val element: T) - example1
boolean flag variable - example1
containsKey for Map<K, T?> - example1
The special UNINITIALIZED_VALUE for representing the second kind of null - example1
The last approach has the best performance, but it's also the most error-prone. So I've decided to encapsulate it in inline class Optional<T>:
inline class Optional<out T> #Deprecated(
message = "Not type-safe, use factory method",
replaceWith = ReplaceWith("Optional.of(_value)")
) constructor(private val _value: Any?) {
val value: T?
get() =
#Suppress("UNCHECKED_CAST")
if (isPresent) _value as T
else null
val isPresent: Boolean
get() = _value != NULL
companion object {
#Suppress("DEPRECATION")
fun <T> of(value: T) = Optional<T>(value)
fun <T : Any> ofNullable(value: T?): Optional<T> =
if (value == null) EMPTY
else of(value)
#Suppress("DEPRECATION")
val EMPTY = Optional<Nothing>(NULL)
}
private object NULL
}
inline fun <T> Optional<T>.ifPresent(code: (T) -> Unit) {
#Suppress("UNCHECKED_CAST")
if (isPresent) return code(value as T)
}
inline fun <T> Optional<T>.or(code: () -> T): T {
ifPresent { return it }
return code()
}
The first problem with this Optional is public constructor, which allows creating instances with arguments of not matching type.
The second problem was noticed at testing time. Here is the failed test:
emptyOr { Optional.EMPTY }.value assertEql null
fun <T> emptyOr(other: () -> T): T = Optional.EMPTY.or(other)
Exception:
Exception ClassCastException: Optional$NULL cannot be cast to Optional
at (Optional.kt:42) // emptyOr { Optional.EMPTY }.value assertEql null
If I remove inline modifier from Optional, the test will pass.
Q: Is there any way to fix these problems without removing inline modifier from Optional?
1 Examples include some context. Please read them fully before writing that I added incorrect links.
I implemented exactly the same utility in one of my projects: OptionalValue.kt. My implementation is very similar to yours, it is also an inline/value class, so it should be cpu/memory efficient and it passes all tests I throw at it.
Regarding your first question: about a public constructor. There is an annotation specifically for this case: #PublishedApi. I tried to reproduce ClassCastException from your example, but it worked for me without problems, so I believe it was a bug in Kotlin itself (?).
Also, to answer the question why do we need double nullability, I explained my point here

Kotlin How to create dynamic Object

In javascript we can do something like this
function putritanjungsari(data){
console.log(data.name)
}
let data = {
name:"putri",
div:"m4th"
}
putritanjungsari(data)
In kotlin, i'am creating a function that accept an object as parameter then read it's properties later, how to do that in kotlin that targeting JVM?
If I understood your question correct, you are trying to have a variable that associates keys with some value or undefined(null in kt) if none are found. You are searching for a Map
If you don't know what types you want, you can make a map of type Any? So
Map<String, Any?>
Which is also nullable
Map<String, Any>
If you don't want nullables
Your code for example:
fun putritanjungsari(data: Map<String, Any?>){
print(data["name"])
}
val data: Map<String, Any?> =mapOf(
"name" to "putri",
"div" to "m4th"
)
putritanjungsari(data)
Note that you can't add new keys or edit any data here, the default map is immutable. There is MutableMap (which is implemented the same, only it has a method to put new data)
You can apply the property design pattern to solve your problem.
Here is its implementation in Kotlin:
interface DynamicProperty<T> {
fun cast(value: Any?): T
fun default(): T
companion object {
inline fun <reified T> fromDefaultSupplier(crossinline default: () -> T) =
object : DynamicProperty<T> {
override fun cast(value: Any?): T = value as T
override fun default(): T = default()
}
inline operator fun <reified T> invoke(default: T) = fromDefaultSupplier { default }
inline fun <reified T> required() = fromDefaultSupplier<T> {
throw IllegalStateException("DynamicProperty isn't initialized")
}
inline fun <reified T> nullable() = DynamicProperty<T?>(null)
}
}
operator fun <T> DynamicProperty<T>.invoke(value: T) = DynamicPropertyValue(this, value)
data class DynamicPropertyValue<T>(val property: DynamicProperty<T>, val value: T)
class DynamicObject(vararg properties: DynamicPropertyValue<*>) {
private val properties = HashMap<DynamicProperty<*>, Any?>().apply {
properties.forEach { put(it.property, it.value) }
}
operator fun <T> get(property: DynamicProperty<T>) =
if (properties.containsKey(property)) property.cast(properties[property])
else property.default()
operator fun <T> set(property: DynamicProperty<T>, value: T) = properties.put(property, value)
operator fun <T> DynamicProperty<T>.minus(value: T) = set(this, value)
}
fun dynamicObj(init: DynamicObject.() -> Unit) = DynamicObject().apply(init)
You can define your properties these ways:
val NAME = DynamicProperty.required<String>() // throws exceptions on usage before initialization
val DIV = DynamicProperty.nullable<String>() // has nullable type String?
val IS_ENABLED = DynamicProperty(true) // true by default
Now you can use them:
fun printObjName(obj: DynamicObject) {
println(obj[NAME])
}
val data = dynamicObj {
NAME - "putri"
DIV - "m4th"
}
printObjName(data)
// throws exception because name isn't initialized
printObjName(DynamicObject(DIV("m4th"), IS_ENABLED(false)))
Reasons to use DynamicObject instead of Map<String, Any?>:
Type-safety (NAME - 3 and NAME(true) will not compile)
No casting is required on properties usage
You can define what the program should do when a property isn't initialized
Kotlin is statically typed language, so it required a param type to be precisely defined or unambiguously inferred (Groovy, for instance, addresses the case by at least two ways). But for JS interoperability Kotlin offers dynamic type.
Meanwhile, in your particular case you can type data structure to kt's Map and do not argue with strict typing.
You have to use Any and after that, you have to cast your object, like this
private fun putritanjungsari(data : Any){
if(data is Mydata){
var data = data as? Mydata
data.name
}
}
Just for the sake of inspiration. In Kotlin, you can create ad hoc objects:
val adHoc = object {
var x = 1
var y = 2
}
println(adHoc.x + adHoc.y)

GSON-based DSL causing a NPE

I've been trying to create a Kotlin DSL for creating GSON JsonObjects with a JSON-like syntax. My builder looks like this
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
init {
builder()
}
val result = JsonObject()
infix fun String.to(property: Number) = result.addProperty(this, property)
infix fun String.to(property: Char) = result.addProperty(this, property)
infix fun String.to(property: Boolean) = result.addProperty(this, property)
infix fun String.to(property: String) = result.addProperty(this, property)
infix fun String.to(property: JsonElement) = result.add(this, property)
infix fun String.to(properties: Collection<JsonElement>) {
val arr = JsonArray()
properties.forEach(arr::add)
result.add(this, arr)
}
operator fun String.invoke(builder: JsonObject.() -> Unit) {
val obj = JsonObject()
obj.builder()
result.add(this, obj)
}
}
fun json(builder: JsonBuilder.() -> Unit) = JsonBuilder(builder).result
And my test looks like this
fun main() {
val json = json {
"name" to "value"
"obj" {
"int" to 1
}
"true" to true
}
println(json)
}
However, upon execution it causes a NullPointerException pointing to the first String extension function used, which I don't find very descriptive as I don't see anything being nullable up to that point. Moreover, I don't see how it really differs from the regular execution which of course doesn't cause a NPE.
val json = JsonObject()
json.addProperty("name", "value")
val obj = JsonObject()
obj.addProperty("int", 1)
json.add("obj", obj)
json.addProperty("true", true)
My question is what's exactly causing the exception (and how to prevent it).
The issue is that you've specified the initialiser block earlier than the result object, causing it to be null when you come to use it - this can be visualised by the following (decompiled output of your code).
public JsonBuilder(#NotNull Function1 builder) {
Intrinsics.checkParameterIsNotNull(builder, "builder");
super();
builder.invoke(this);
this.result = new JsonObject();
}
Therefore, the solution is to move the declaration and initialisation of result earlier than the initialiser block.
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
val result = JsonObject()
init {
builder()
}
// ...
}
And the result is now...
{"name":"value","int":1,"obj":{},"true":true}
EDIT: You'll also want to allow chaining with your DSL, and fix a bug you currently have.
operator fun String.invoke(builder: JsonBuilder.() -> Unit) {
val obj = JsonBuilder(builder).result
result.add(this, obj)
}
Which produces the correct result of
{"name":"value","obj":{"int":1},"true":true}