Kotlin invoke getter/setter reflectively - reflection

Beginner in Kotlin here.
I try to create and populate objects by reflection in a program. I cannot find the equivalent functionality in pure kotlin so my solution resembles the code below which works fine, but requires the use of dirty references like java.lang.String::class.java and intelliJ, understandably, doesn't seem to like this. Is there a simpler way that I am missing to do this?
val jclass = myObject::class.java
val setters = jclass.declaredMethods.filter { it.name.startsWith("set") }
for (s in setters) {
val paramType = s.parameterTypes.first()
val data = when(paramType) {
java.lang.Integer::class.java -> foo
java.lang.Double::class.java -> bar
java.lang.String::class.java -> baz
}
s.invoke(myObject, data)
}

You can use Kotlin reflection, which requires you to add kotlin-reflect as a dependency to your project.
Here you can find kotlin-reflect for Kotlin 1.0.5, or pick another version if you use different Kotlin version.
After that, you can rewrite your code as follows:
val properties = myObject.javaClass.kotlin.memberProperties
for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
val data = when (p.returnType.javaType) {
Int::class.javaPrimitiveType,
Int::class.javaObjectType -> foo
Double::class.javaPrimitiveType,
Double::class.javaObjectType -> bar
String::class.java -> baz
else -> null
}
if (data != null)
p.setter.call(myObject, data)
}
Some details:
Despite using Kotlin reflection, this approach works with Java classes as well, their fields and accessors will be seen as properties, as described here.
Just like with Java reflection, memberProperties returns public properties of this type and all its supertypes. To get all the properties declared in the type (including the private ones, but not those from the supertypes), use declaredMemberProperties instead.
.filterIsInstance<KMutableProperty<*> returns only the mutable properties, so that you can use their p.setter later. If you need to iterate over the getters of all the properties, remove it.
In the when block, I compared p.returnType.javaType to Int::class.javaPrimitiveType and Int::class.javaObjectType, because what's Int in Kotlin can be mapped to either Java int or java.lang.Integer depending on its usage. In Kotlin 1.1, it will be enough to check p.returnType.classifier == Int::class.

If You need to get property getter/setter, there is a couple of built-in constructions for it YourClass::propertyName
have a look at example bellow
fun main(args: Array<String>) {
val myObject = Cat("Tom", 3, 35)
println(Cat::age.getter.call(myObject)) // will print 3
Cat::age.setter.call(myObject, 45)
print(myObject) // will print Cat(name=Tom, age=45, height=35)
}
data class Cat(var name : String, var age : Int, val height : Int)
but sometimes you don't know class exactly(working with generics) or need to get list of properties, then use val <T : Any> KClass<T>.declaredMemberProperties: Collection<KProperty1<T, *>> it will return all properties, some of them can be mutable(var) and some immutable(val), you can find out immutability by checking belonging to KMutableProperty<*> (by filtering with is operator or using convenience methods such as filterIsInstance<KMutableProperty<*>>)
about your code snippet
I absolutely agree with hotkey, but now it is better to use myObject::class.declaredMemberProperties instead of myObject.javaClass.kotlin.memberProperties
because the second one is deprecated
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html
data class Cat(var name : String, var age : Int, val height : Int)
#JvmStatic
fun main(args: Array<String>) {
val myObject = Cat("Tom", 3, 35)
val properties = myObject::class.declaredMemberProperties
for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
val data = when (p.returnType.javaType) {
Int::class.javaPrimitiveType,
Int::class.javaObjectType -> 5
String::class.java -> "Rob"
else -> null
}
if (data != null)
p.setter.call(myObject, data)
}
println(myObject)
// it will print Cat(name=Rob, age=5, height=35),
// because height isn't var(immutable)
}
in general, I would approach similar problems with such construction in mind
val myObject = Cat("Tom", 3, 35)
Cat::class.declaredMemberProperties
//if we want only public ones
.filter{ it.visibility == KVisibility.PUBLIC }
// We only want strings
.filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
.filterIsInstance<KMutableProperty<*>>()
.forEach { prop ->
prop.setter.call(myObject, "Rob")
}
println(myObject)
//it will print Cat(name=Rob, age=3, height=35),
//because name is only eligible in this case

Related

Convert to readonly collection with AutoMapper

I need to find a way to convert list of arbitrary values to another list
AutoMapper works if destination type is ICollection<> because it's creating an instance and populating it with Add, but my type is immutable list 'a list
So if I create list of ints:
let ints = [1; 2; 3]
And try to map it to ResizeArray<int64> (synonym to List<T>) with
mapper.Map<ResizeArray<int64>>(ints)
it will work, but if I try to map it to int64 list with
mapper.Map<int64 list>
then it will fail.
I've found a solution that will convert successfully, but it will work only with explicitly defined types
let cfg = MapperConfiguration(
fun c ->
c.CreateMap<int, int64>() |> ignore
c.CreateMap<int list, int64 list>()
.ConvertUsing(
fun source _ (cfg: ResolutionContext) ->
source
|> Seq.map cfg.Mapper.Map<int, int64>
|> Seq.toList))
So question is: How to write type converter that will convert 'a list to 'b list without explicitly defining all possible combinations of these types?
I've finally found solution. All I needed is to look at source code of this ReadOnlyCollection mapper
Solution is not perfect, because collection items transformed and inserted into System.Collections.Generic.List and afterwards converted to Microsoft.FSharp.Collections.FSharpList, which have some overhead. But at least it's working
using System.Collections.Generic;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using AutoMapper.Mappers;
using AutoMapper.Internal;
using static AutoMapper.Internal.CollectionMapperExpressionFactory;
using Microsoft.FSharp.Collections;
public class SeqToFSharpListMapper : EnumerableMapperBase
{
public override bool IsMatch(TypePair context)
=> context.SourceType.IsEnumerableType()
&& context.DestinationType.FullName.StartsWith("Microsoft.FSharp.Collections.FSharpList`1");
public override Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap,
Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
var listType = typeof(List<>).MakeGenericType(ElementTypeHelper.GetElementType(destExpression.Type));
var list = MapCollectionExpression(configurationProvider, profileMap, memberMap, sourceExpression, Default(listType), contextExpression, typeof(List<>), MapItemExpr);
return Call(typeof(ListModule).GetMethod(nameof(ListModule.OfSeq)).MakeGenericMethod(destExpression.Type.GenericTypeArguments[0]), list);
}
}
And F#
override _.MapExpression (configurationProvider, profileMap, memberMap, sourceExpression, destExpression, contextExpression) =
let listType = typedefof<System.Collections.Generic.List<_>>.MakeGenericType(ElementTypeHelper.GetElementType destExpression.Type)
let list = MapCollectionExpression(configurationProvider, profileMap, memberMap,
sourceExpression, Default(listType), contextExpression,
typedefof<System.Collections.Generic.List<_>>,
MapItem(fun c p s d ctx i -> MapItemExpr(c, p, s, d, ctx, &i))) // compiler require explicit lambda
upcast Call(typedefof<obj list>.Assembly // don't want to use AssemblyQualifiedName
.GetType("Microsoft.FSharp.Collections.ListModule") // have to use this trick because we can't access ListModule through typeof
.GetMethod("OfSeq")
.MakeGenericMethod(destExpression.Type.GenericTypeArguments.[0]),
list)

Corda vault batch query by arbitrary indexed object property

looking for help regarding queries. if i'm looking for the latest LinearState of SomeObjectState by property someProp i can do the following:
private fun lookupBySomeProp(someProp: String) : List<StateAndRef<SomeObjectState>> {
val generalCriteria = VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)
val somePropIdx = builder { SomePersistentObject::someProp.equal(someProp) }
val somePropQueryCriteria = VaultCustomQueryCriteria(somePropIdx)
val queryCriteria = generalCriteria.and(somePropQueryCriteria)
val results = serviceHub.vaultService.queryBy<SomeObjectState>(queryCriteria)
// ... handle results here
}
how would i go about doing a single batch lookup for multiple SomeObjectState objects given a list of someProp identifiers?
private fun lookupBySomeProps(somePropList: List<String>) : List<StateAndRef<SomeObjectState>> {
val generalCriteria = VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)
// how do i build efficiently build my bulk query?
}
i'm expecting to have to do something along the lines of the following in order return multiple states matching the list but have been have trouble properly implementing it
val somePropeIdx = builder { SomePersistentObject::someProp.in(somePropList) }
From this link: https://docs.corda.net/api-vault-query.html you can use .in() in your criteria among many other things:
Binary logical (AND, OR)
Comparison (LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL)
Equality (EQUAL, NOT_EQUAL)
Likeness (LIKE, NOT_LIKE)
Nullability (IS_NULL, NOT_NULL)
Collection based (IN, NOT_IN)
Standard SQL-92 aggregate functions (SUM, AVG, MIN, MAX, COUNT)
The reason why your query in the question didn't work is because how Corda has defined the .in() function.
You had:
val somePropeIdx = builder { SomePersistentObject::someProp.in(somePropList) }
Substituting notIn works fine:
val somePropeIdx = builder { SomePersistentObject::someProp.notIn(somePropList) }
However, in order to use .in() you have to include backticks.
So your query would be:
val somePropeIdx = builder { SomePersistentObject::someProp.`in`(somePropList) }
You can see this difference in definition in the source here.

Is there a way to filter out null Any? values in Kotlin Map?

I'm trying to think of a function that would allow a Map<String, Any?> object to be treated as Map<String,Any> through type inference through applying a single function.
I am pretty new to the transformation functions in Kotlin and have tried the various filter and filterValues filterNot on the map like so:
val input = mapOf(Pair("first",null))
val filtered: Map<String,Any> = input.filter { it.value!=null }
it also fails to compile with any of these
input.filterValues { it!=null }
input.filterNot { it.value==null }
input.filterNot { it.value is Nothing }
The closest I can seem to get is applying multiple steps or having an Unchecked cast warning. I would have thought that filtering the values to be !=null would suffice. My only other thought is that it's due to the generics?
The filter functions return a Map with the same generic types as the original map. To transform the type of the value, you need to map the values from Any? to Any, by doing a cast. The compiler can't know that the predicate you pass to filter() makes sure all the values of the filtered map are non-null, so it can't use type inference. So your best et is to use
val filtered: Map<String, Any> = map.filterValues { it != null }.mapValues { it -> it.value as Any }
or to define a function doing the filtering and the transformation in a single pass, and thus be able to use smart casts:
fun filterNotNullValues(map: Map<String, Any?>): Map<String, Any> {
val result = LinkedHashMap<String, Any>()
for ((key, value) in map) {
if (value != null) result[key] = value
}
return result
}
The compiler just doesn't perform type analysis deep enough to infer that, for example, input.filterValues { it != null } filters out null values from the map and thus the resulting map should have a not-null value type. Basically there can be arbitrary predicate with arbitrary meaning in terms of types and nullability.
There is no special case function for filtering null values out of a map in the stdlib (like there is .filterIsInstance<T>() for iterables). Therefore your easiest solution is to apply an unchecked cast thus telling the compiler that you are sure about the type safety not being violated:
#Suppress("UNCHECKED_CAST")
fun <K, V> Map<K, V?>.filterNotNullValues() = filterValues { it != null } as Map<K, V>
See also: another question with a similar problem about is-check.
This yields no warnings kotlin 1.5.30
listOfNotNull(
nullableString?.let { "key1" to it },
nullableString?.let { "key2" to it }
).toMap()

Kotlin's List missing "add", "remove", Map missing "put", etc?

In Java we could do the following
public class TempClass {
List<Integer> myList = null;
void doSomething() {
myList = new ArrayList<>();
myList.add(10);
myList.remove(10);
}
}
But if we rewrite it to Kotlin directly as below
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
myList!!.add(10)
myList!!.remove(10)
}
}
I got the error of not finding add and remove function from my List
I work around casting it to ArrayList, but that is odd needing to cast it, while in Java casting is not required. And that defeats the purpose of having the abstract class List
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
(myList!! as ArrayList).add(10)
(myList!! as ArrayList).remove(10)
}
}
Is there a way for me to use List but not needing to cast it, like what could be done in Java?
Unlike many languages, Kotlin distinguishes between mutable and immutable collections (lists, sets, maps, etc). Precise control over exactly when collections can be edited is useful for eliminating bugs, and for designing good APIs.
https://kotlinlang.org/docs/reference/collections.html
You'll need to use a MutableList list.
class TempClass {
var myList: MutableList<Int> = mutableListOf<Int>()
fun doSomething() {
// myList = ArrayList<Int>() // initializer is redundant
myList.add(10)
myList.remove(10)
}
}
MutableList<Int> = arrayListOf() should also work.
Defining a List collection in Kotlin in different ways:
Immutable variable with immutable (read only) list:
val users: List<User> = listOf( User("Tom", 32), User("John", 64) )
Immutable variable with mutable list:
val users: MutableList<User> = mutableListOf( User("Tom", 32), User("John", 64) )
or without initial value - empty list and without explicit variable type:
val users = mutableListOf<User>()
//or
val users = ArrayList<User>()
you can add items to list:
users.add(anohterUser) or
users += anotherUser (under the hood it's users.add(anohterUser))
Mutable variable with immutable list:
var users: List<User> = listOf( User("Tom", 32), User("John", 64) )
or without initial value - empty list and without explicit variable type:
var users = emptyList<User>()
NOTE: you can add* items to list:
users += anotherUser - *it creates new ArrayList and assigns it to users
Mutable variable with mutable list:
var users: MutableList<User> = mutableListOf( User("Tom", 32), User("John", 64) )
or without initial value - empty list and without explicit variable type:
var users = emptyList<User>().toMutableList()
//or
var users = ArrayList<User>()
NOTE: you can add items to list:
users.add(anohterUser)
but not using users += anotherUser
Error: Kotlin: Assignment operators ambiguity:
public operator fun Collection.plus(element: String): List defined in kotlin.collections
#InlineOnly public inline operator fun MutableCollection.plusAssign(element: String): Unit defined in kotlin.collections
see also:
https://kotlinlang.org/docs/reference/collections.html
Agree with all above answers of using MutableList but you can also add/remove from List and get a new list as below.
val newListWithElement = existingList + listOf(element)
val newListMinusElement = existingList - listOf(element)
Or
val newListWithElement = existingList.plus(element)
val newListMinusElement = existingList.minus(element)
Apparently, the default List of Kotlin is immutable.
To have a List that could change, one should use MutableList as below
class TempClass {
var myList: MutableList<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
myList!!.add(10)
myList!!.remove(10)
}
}
Updated
Nonetheless, it is not recommended to use MutableList unless for a list that you really want to change. Refers to https://hackernoon.com/read-only-collection-in-kotlin-leads-to-better-coding-40cdfa4c6359 for how Read-only collection provides better coding.
In Kotlin you must use MutableList or ArrayList.
Let's see how the methods of MutableList work:
var listNumbers: MutableList<Int> = mutableListOf(10, 15, 20)
// Result: 10, 15, 20
listNumbers.add(1000)
// Result: 10, 15, 20, 1000
listNumbers.add(1, 250)
// Result: 10, 250, 15, 20, 1000
listNumbers.removeAt(0)
// Result: 250, 15, 20, 1000
listNumbers.remove(20)
// Result: 250, 15, 1000
for (i in listNumbers) {
println(i)
}
Let's see how the methods of ArrayList work:
var arrayNumbers: ArrayList<Int> = arrayListOf(1, 2, 3, 4, 5)
// Result: 1, 2, 3, 4, 5
arrayNumbers.add(20)
// Result: 1, 2, 3, 4, 5, 20
arrayNumbers.remove(1)
// Result: 2, 3, 4, 5, 20
arrayNumbers.clear()
// Result: Empty
for (j in arrayNumbers) {
println(j)
}
UPDATE: As of Kotlin 1.3.70, the exact buildList function below is available in the standard library as an experimental function, along with its analogues buildSet and buildMap. See https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/.
Confining Mutability to Builders
The top answers here correctly speak to the difference in Kotlin between read-only List (NOTE: it's read-only, not "immutable"), and MutableList.
In general, one should strive to use read-only lists, however, mutability is still often useful at construction time, especially when dealing with third-party libraries with non-functional interfaces. For cases in which alternate construction techniques are not available, such as using listOf directly, or applying a functional construct like fold or reduce, a simple "builder function" construct like the following nicely produces a read-only list from a temporary mutable one:
val readonlyList = mutableListOf<...>().apply {
// manipulate your list here using whatever logic you need
// the `apply` function sets `this` to the `MutableList`
add(foo1)
addAll(foos)
// etc.
}.toList()
and this can be nicely encapsulated into a re-usable inline utility function:
inline fun <T> buildList(block: MutableList<T>.() -> Unit) =
mutableListOf<T>().apply(block).toList()
which can be called like this:
val readonlyList = buildList<String> {
add("foo")
add("bar")
}
Now, all of the mutability is isolated to one block scope used for construction of the read-only list, and the rest of your code uses the read-only list that is output from the builder.
You can do with create new one like this.
var list1 = ArrayList<Int>()
var list2 = list1.toMutableList()
list2.add(item)
Now you can use list2, Thank you.
https://kotlinlang.org/docs/reference/collections.html
According to above link List<E> is immutable in Kotlin.
However this would work:
var list2 = ArrayList<String>()
list2.removeAt(1)
A list is immutable by Default, you can use ArrayList instead. like this :
val orders = arrayListOf<String>()
then you can add/delete items from this like below:
orders.add("Item 1")
orders.add("Item 2")
by default ArrayList is mutable so you can perform the operations on it.
In concept of immutable data, maybe this is a better way:
class TempClass {
val list: List<Int> by lazy {
listOf<Int>()
}
fun doSomething() {
list += 10
list -= 10
}
}

instantiate object with reflection using constructor arguments

I'm trying to figure out how to instantiate a case class object with reflection. Is there any support for this? The closest I've come is looking at scala.reflect.Invocation, but this seems more for executing methods that are a part of an object.
case class MyClass(id:Long, name:String)
def instantiate[T](className:String)(args:Any*) : T = { //your code here }
Is close to the API I'm looking for.
Any help would be appreciated.
scala> case class Foo(id:Long, name:String)
defined class Foo
scala> val constructor = classOf[Foo].getConstructors()(0)
constructor: java.lang.reflect.Constructor[_] = public Foo(long,java.lang.String)
scala> val args = Array[AnyRef](new java.lang.Integer(1), "Foobar")
args: Array[AnyRef] = Array(1, Foobar)
scala> val instance = constructor.newInstance(args:_*).asInstanceOf[Foo]
instance: Foo = Foo(1,Foobar)
scala> instance.id
res12: Long = 1
scala> instance.name
res13: String = Foobar
scala> instance.getClass
res14: java.lang.Class[_] = class Foo
Currently there is not much reflection support in Scala. But you can fall back to th Java Reflection API. But there are some obstacles:
You have to create a Array[AnyRef] and box your "primitive types" in the wrapper classes (java.lang.Integer, java.lang.Character, java.lang.Double, ...)
newInstance(Object ... args) gets an varargs array of Object, so you should give the type inferer a hint with :_*
newInstance(...) returns an Object so you have to cast it back with asInstanceOf[T]
The closest I could get to your instantiate function is this:
def instantiate(clazz: java.lang.Class[_])(args:AnyRef*): AnyRef = {
val constructor = clazz.getConstructors()(0)
return constructor.newInstance(args:_*).asInstanceOf[AnyRef]
}
val instance = instantiate(classOf[MyClass])(new java.lang.Integer(42), "foo")
println(instance) // prints: MyClass(42,foo)
println(instance.getClass) // prints: class MyClass
You cannot get the get class from a generic type. Java erases it (type erasure).
Edit: 20 September 2012
Three years on, the instantiate method can be improved to return a properly typed object.
def instantiate[T](clazz: java.lang.Class[T])(args:AnyRef*): T = {
val constructor = clazz.getConstructors()(0)
return constructor.newInstance(args:_*).asInstanceOf[T]
}
See http://www.nabble.com/How-do-I-get-the-class-of-a-Generic--td20873455.html
See answers to Scala: How do I dynamically instantiate an object and invoke a method using reflection? as well, especially regarding type erasure.
This is what I've ended up with so far, I'd like to not have to deal directly with AnyRef if possible. So if anyone knows a way to get around that I'd appreciate the help.
case class MyClass(id:Long,name:String)
def instantiate[T](classArgs: List[AnyRef])(implicit m : Manifest[T]) : T ={
val constructor = m.erasure.getConstructors()(0)
constructor.newInstance(classArgs:_*).asInstanceOf[T]
}
val list = List[AnyRef](new java.lang.Long(1),"a name")
val result = instantiate[MyClass](list)
println(result.id)

Resources