I'm restoring complex data from json files and some of them requires call for specific types that does not have empty constructors, but constructors with default parameters.
There is a method for creation an empty object,
abstract class Restorer {
inline fun <reified T>load(ctx: T): T {
var that: T = reset(ctx)
// ...
}
inline fun <reified T>reset(ctx: T): T {
val primaryConstructorT = T::class.constructors.find {
it.parameters.isEmpty() || it.parameters.all { prm -> prm.isOptional }
}
return primaryConstructorT!!.call() // <--- here is a problem
}
}
So in some cases primaryConstructorT is a reflection for constructor with optional params, but direct call for that produces an exception.
Callable expects 2 arguments, but 0 were provided.
There is the case for creation simple data class
data class DataClass (val foo: List<String> = listOf(), val bar: List<Int> = listOf())
// ...
var context: DataClass? = null;
// ...
context = Restorer.load(context)
Is there any method to call it
Maybe there is a better way, but you can use callBy() with an empty map:
return primaryConstructorT!!.callBy(emptyMap())
It automatically replaces missing parameters with their defaults.
Related
I have a situation where I need to create a copy of data class object. I don't know in advance which of the many data classes I have will come in into the function. I do know, however, that only data classes will be used as input to this function.
This is what didn't work:
fun doSomething(obj: Any): Any {
obj.copy(...) // <- there's no 'copy' on Any
...
}
This is what I really like to do:
fun doSomething(obj: KAnyDataClass): KAnyDataClass {
obj.copy(...) // <- works, data classes have a 'copy' method
...
}
I'm not a Kotlin developer, but it looks like the language does not support dynamic dispatch or traits. You might find success with the dynamic type, which just turns off the type-checker so it won't yell at you for using a method that it doesn't know about. However this opens up the possibility of a runtime error if you pass an argument that actually doesn't have that method.
There is no class or interface for data classes, but we know from the documentation of data classes that there are derived functions componentN and copy in each data class.
We can use that knowledge to write an abstract copy method that calls the copy method of a given arbitrary data class using reflection:
fun <T : Any> copy(data: T, vararg override: Pair<Int, Any?>): T {
val kClass = data::class
if (!kClass.isData) error("expected a data class")
val copyFun = kClass.functions.first { it.name == "copy" }
checkParameters(override, kClass)
val vals = determineComponentValues(copyFun, kClass, override, data)
#Suppress("UNCHECKED_CAST")
return copyFun.call(data, *vals) as T
}
/** check if override of parameter has the right type and nullability */
private fun <T : Any> checkParameters(
override: Array<out Pair<Int, Any?>>,
kClass: KClass<out T>
) {
override.forEach { (index, value) ->
val expectedType = kClass.functions.first { it.name == "component${index + 1}" }.returnType
if (value == null) {
if (!kClass.functions.first { it.name == "component${index + 1}" }.returnType.isMarkedNullable) {
error("value for parameter $index is null but parameter is not nullable")
}
} else {
if (!expectedType.jvmErasure.isSuperclassOf(value::class))
error("wrong type for parameter $index: expected $expectedType but was ${value::class}")
}
}
}
/** determine for each componentN the value from override or data element */
private fun <T : Any> determineComponentValues(
copyFun: KFunction<*>,
kClass: KClass<out T>,
override: Array<out Pair<Int, Any?>>,
data: T
): Array<Any?> {
val vals = (1 until copyFun.parameters.size)
.map { "component$it" }
.map { name -> kClass.functions.first { it.name == name } }
.mapIndexed { index, component ->
override.find { it.first == index }.let { if (it !== null) it.second else component.call(data) }
}
.toTypedArray()
return vals
}
Since this copy function is generic and not for a specific data class, it is not possible to specify overloads in the usual way, but I tried to support it in another way.
Let's say we have a data class and element
data class Example(
val a: Int,
val b: String,
)
val example: Any = Example(1, "x")
We can create a copy of example with copy(example) that has the same elements as the original.
If we want to override the first element, we cannot write copy(example, a = 2), but we can write copy(example, 0 to 2), saying that we want to override the first component with value 2.
Analogously we can write copy(example, 0 to 3, 1 to "y") to specify that we want to change the first and the second component.
I am not sure if this works for all cases since I just wrote it, but it should be a good start to work with.
I have an the following Interface
interface Filter {
fun checkFor(message: Message): Boolean = message.predicate()
fun Message.predicate(): Boolean
infix fun and(otherFilter: Filter): Filter = object : Filter {
override fun Message.predicate(): Boolean =
this#Filter.checkFor(this) && otherFilter.checkFor(this)
}
infix fun or(otherFilter: Filter): Filter = object : Filter {
override fun Message.predicate(): Boolean =
this#Filter.checkFor(this) || otherFilter.checkFor(this)
}
operator fun not(): Filter = object : Filter {
override fun Message.predicate(): Boolean = !this#Filter.checkFor(this)
}
class Custom(private val customPredicate: Message.() -> Boolean) : Filter {
override fun Message.predicate(): Boolean = customPredicate()
}
object All : Filter {
override fun Message.predicate(): Boolean = true
}
In this Interface I have a class named "Custom"
which ask in the constructor for
class Custom(private val customPredicate: Message.() -> Boolean)
And I have no idea how should I use this class to create my own Filter
Please assist
There are quite a few ways you can create an object of Custom class.
You can use the lambda expression as a function with the receiver, the receiver is available inside the body as context.
val custom = Custom { //lambda begins here
//here you can call any method of Message, which is the receiver
}
You can create an extension function of Message and pass its reference to the Custom class constructor.
fun Message.customPredicate() {
// Your code here
}
Pass reference of this function to the constructor of the Custom class
val custom = Custom(Message::customPredicate)
You can also use an anonymous function for creating an object of the Custom class.
val custom = Custom(fun Message.() {
// Your code here
})
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)
I'm attempting to write an invocation handler that uses a map (supplied at runtime) to implement an interface's getters.
This very crudely works. I know the basic types that may be returned, so I'm OK with having a when expression.
I haven't found a way to avoid using the name of the class as the subject of the when expression; is there a better way?
class DynamicInvocationHandler<T>(private val delegate: Map<String, Any>, clzz: Class<T>) : InvocationHandler {
val introspector = Introspector.getBeanInfo(clzz)
val getters = introspector.propertyDescriptors.map { it.readMethod }
override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
if (method in getters) {
// get the value from the map
val representation = delegate[method.name.substring(3).toLowerCase()]
// TODO need better than name
when (method.returnType.kotlin.simpleName) {
LocalDate::class.simpleName -> {
val result = representation as ArrayList<Int>
return LocalDate.of(result[0], result[1], result[2])
}
// TODO a few other basic types like LocalDateTime
// primitives come as they are
else -> return representation
}
}
return null
}
}
You can use the types instead of the class names in the when statement. After a type is matched, Kotlin smart cast will automatically cast it
Example
val temporal: Any? = LocalDateTime.now()
when (temporal){
is LocalDate -> println("dayOfMonth: ${temporal.dayOfMonth}")
is LocalTime -> println("second: ${temporal.second}")
is LocalDateTime -> println("dayOfMonth: ${temporal.dayOfMonth}, second: ${temporal.second}")
}
when expressions support any type (unlike Java's switch), so you can just use the KClass instance itself:
when (method.returnType.kotlin) {
LocalDate::class -> {
...
}
...
}
I tried this, and the code didn't compile.
class GenericClass<T>() {
private var arr : Array<T>? = null
{
arr = Array<T>(10, { null })
}
}
There are two compiler errors reported in this code: one is about nullable types and another about generics.
Nullable types. Kotlin enforces a discipline of nullable references, and since T may be instantiated with, say, String making arr be of type Array, the compiler does not allow you to put nulls into this array. If you want nulls, you have to change the type to Array:
class GenericClass<T>() {
private var arr : Array<T?>? = null
{
arr = Array(10, { null }) // No need to specify type arguments again
}
}
Generics. The example above still has a compile-time error, because we are trying to construct an array of an unknown type T. Note that this problem exists in Java as well. Kotlin being compiled to JVM byte code entails two things:
generics type arguments are erased at runtime,
except for generic arguments of arrays.
This means that in the byte code Kotlin has to create an array of some concrete type, and not an unknown type T. It could create arrays of Objects whenever it sees Array, but this would not work, for example, in this case:
fun test() {
fun foo(srts: Array<String?>) {
// ...
}
val gc = GenericClass<String>()
foo(gc.arr)
}
Here, in the last line, we are trying to pass Object[] where String[] is expected, and get a runtime error.
This is why Kotlin refuses to create arrays of T. You can work around this problem by explicitly suppressing the type system, i.e. by using type casts:
class GenericClass<T>() {
val arr : Array<T?>
{
arr = Array<Any?>(10, { null }) as Array<T?>
}
}
Here we explicitly request creation of an array of Any (compiled to Object[]), and then type-cast it to an array of T. The compiler issues a warning, but obeys our will.
Note that the problematic example above remains, i.e. if you pass the array created this way where an array of strings is expected, it ill fail at run time.
method
val array : Array<T?> = kotlin.arrayOfNulls<T>(size)
from docs
/**
*Returns an array of objects of the given type with the given [size],
*initialized with null values.
*/
public fun <reified #PureReifiable T> arrayOfNulls(size: Int): Array<T?>
If you need to initialize array in the constructor, you can add an inline factory method and parametrize it using reified T. This solution is inspired by answer https://stackoverflow.com/a/41946516/13044086
class GenericClass<T> protected constructor(
private val arr : Array<T?>
) {
companion object {
inline fun <reified T>create(size: Int) = GenericClass<T>(arrayOfNulls(size))
}
}
fun main() {
val strs = GenericClass.create<String>(10)
...
}
Notice that the constructor is protected, because inline function can't access a private constructor.
If you need to create an array after the object is created, you can pass lambda that creates the array into the method. Lambda can be created inside of extension function, so information about type of the array is preserved. #PublishedApi annotation is used to encapsulate private method fill.
import GenericClass.Companion.fill
class GenericClass<T> {
private var arr : Array<T?>? = null
fun show() {
print(arr?.contentToString())
}
private fun fill(arrayFactory: (size: Int) -> Array<T?>) {
this.arr = arrayFactory(10)
}
#PublishedApi
internal fun `access$fill`(arrayFactory: (size: Int) -> Array<T?>) = fill(arrayFactory)
companion object {
inline fun <reified T>GenericClass<T>.fill() {
`access$fill`(arrayFactory = { size -> arrayOfNulls(size) })
}
}
}
fun main() {
val strs = GenericClass<String>()
strs.fill()
strs.show()
}
You could use a helper function as below:
#Suppress("UNCHECKED_CAST")
fun <T> genericArrayOfNulls(size: Int): Array<T?> {
return arrayOfNulls<Any?>(size) as Array<T?>
}