Let's say I'd want to instantiate an object of class A by copying values from class B which is a common practice when, for example, mapping DTO's. To accomplish this in Java or Groovy I'd create a static method on the appropriate DTO with the signature of fromB(A a) and then copy values either with a.val = b.val... in Java or using a.with { val = b.val... } in Groovy.
In Kotlin I've noticed that instance.apply{} is very similar to Groovy's with in that it allows me to directly access the object variables without constantly refering to the object itself since the reference seems to be implied within the closure.
However I've ran into a weird and unexpected error when using apply within companion objects. If I use A().apply {} inside a function of A's companion object I get an error Expression is inaccessible from a nested class 'Companion', use 'inner' keyword to make the class inner Which is weird since I'm calling apply directly on an instance of an object and would thus expect that I should always be able to access it's public properties. Not to mention that it seems like companion objects cannot be set to be inner thus the suggestion in the error message isn't all too helpful.
Here's the full example code:
fun main(args: Array<String>) {
val b = B("Hello", "World")
val a = A.fromB(b)
print("$a.value1 $a.value2")
}
class A() {
var value1: String? = null
var value2: String? = null
companion object {
//This fails with "Expression is inaccessible from a nested class 'Companion', use 'inner' keyword to make the class inner"
fun fromB(b: B): A {
return A().apply {
value1 = b.value3
value2 = b.value4
}
}
}
}
class B(val value3: String, val value4: String) {}
//This works
fun bToA(b: B): A {
return A().apply {
value1 = b.value3
value2 = b.value4
}
}
What is going on here? What am I doing wrong?
This looks like a bug to me. Probably something to do with inline functions (e.g. apply) and companion objects. I suggest searching the JetBrains Bug & Issue Tracker and if you don't find something similar to this create a new issue.
In the meantime I see some alternatives:
Use this (not ideal):
fun fromB(b: B): A {
return A().apply {
this.value1 = b.value3
this.value2 = b.value4
}
}
Move value1 and value2 to A's primary constructor and change fromB(B) to use named arguments (this will still let you define defaults, skip properties when copying, etc.):
class A(var value1: String? = null, var value2: String? = null) {
companion object {
fun fromB(b: B): A {
return A(
value1 = b.value3,
value2 = b.value4
)
}
}
}
UPDATE: In addition to the above you can use b with with:
fun fromB(b: B) = with(b) {
A(
value1 = value3,
value2 = value4
)
}
#MrPlow
I think this is more straightforward way to do, what you want:
fun B.toA(): A {
val self = this;
return A().apply {
value1 = self.value3
value2 = self.value4
}
}
Compare with your example:
val b = B("Hello", "World")
val a = A.fromB(b)
// vs
val a = b.toA();
Related
Suppose we have two classes A and B. The framework will require that A will have 0 or more properties that are of type B. The user can have a reference to an instance of B. For simplicity, assume that type B can only be declared within class A.
class A {
private val myFirstB = B(this)
private val mySecondB = B(this)
val listOfB: List<B> = mutableListOf(myFirstB, mySecondB)
}
class B(a: A) {
val myA : A = a
fun doSomething(){
// great stuff
}
}
fun testIt(){
val a = A()
val b1 = a.listOfB[0]
}
Through Kotlin reflection how can we determine the name of the property that is holding the reference to b1 within A when we only have the reference b1. We also know that B has a reference to A. Through the instance B, we can get the instance of A. Using reflection, we can get the properties of A via the declaredMemberProperties property. This can be iterated through to get the names of all the properties. However, I do not understand how to ensure that the name that is retrieved is associated with the reference b1.
As said in comments, your case looks very specific and usually reflection isn't ideal for such cases. The algorithm either has to target this very specific case, including the fact the b1 is always inside a list. Or it would have to support many different cases and "guess" where to look for b1.
Basically, the idea is to get the value of each member and compare it to the searched value:
fun main() {
val a = A()
val b1 = a.listOfB[0]
// prints: "listOfB"
println(findPropertyNameByValue(a, b1))
}
fun findPropertyNameByValue(owner: Any, value: Any): String {
return owner::class.memberProperties.first { prop ->
#Suppress("UNCHECKED_CAST")
val list = (prop as KProperty1<Any, *>).get(owner)
list is List<*> && list.any { it === value }
}.name
}
We can even get "listOfB" from the b1 alone, then the algorithm would have to iterate through members twice, again by guessing if the value is our A or not. But it is technically possible.
After some experimentation, the getter call holds the reference that is needed. Here is some possible code:
import kotlin.reflect.KClass
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.KProperty1
fun main(){
val a = A()
val b1 = a.listOfB[0]
println(b1)
val clz: KClass<out A> = a::class
val properties: Collection<KProperty1<out A, *>> = clz.declaredMemberProperties
for(p in properties){
println(p)
println(p.name)
println(p.getter.call(a))
if (p.getter.call(a)?.equals(b1) == true){
println("This is b1")
}
}
}
class A {
val myFirstB = B(this)
val mySecondB = B(this)
val listOfB: List<B> = mutableListOf(myFirstB, mySecondB)
}
var cnt = 1
class B(a: A) {
val myA : A = a
val s = cnt++
fun doSomething(){
// great stuff
}
}
This has the following output:
ksl.B#4590c9c3
val ksl.A.listOfB: kotlin.collections.List<ksl.B>
listOfB
[ksl.B#4590c9c3, ksl.B#d62fe5b]
val ksl.A.myFirstB: ksl.B
myFirstB
ksl.B#4590c9c3
This is b1
val ksl.A.mySecondB: ksl.B
mySecondB
ksl.B#d62fe5b
As can be seen, the test of if the getter reports that it correctly finds b1
I am new to Kotlin. Can someone tell me how can I set the value only if not null here in below cade. There should be a way to use with let() but I am not sure how to do it.
If the var2 is not null only I should set it. Otherwise null pointer error will throw.
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.setType2(var2) // how to set var2 only if not null?
.build()
clientClass.send(request)
}
If each builder function returns a new Builder instance, use run:
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.run { if(var2 != null) setType2(var2) else this }
.build()
clientClass.send(request)
}
If the builder functions mutate and return the same Builder instance, it’s simpler to use apply
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.apply { if(var2 != null) setType2(var2) }
.build()
clientClass.send(request)
}
// or more cleanly using apply for everything instead of chaining:
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder().apply {
setType1(var1)
if(var2 != null) setType2(var2)
build()
}
clientClass.send(request)
}
Example of a Builder class whose functions return new instances:
fun setType2(type2: Type2): Builder {
return CombinedBuilder(this, type2) // a new object
}
Example of a Builder class whose functions return the same instance:
fun setType2(type2: Type2): Builder {
this.type2 = type2
return this // the same object
}
The second type is more common, but sometimes the first type is used. You might have to check the source code to know for sure. If you can't be sure, use the .run method because it will work for either.
Using scope functions is one way to solve this but results in code that is hard to read imo. If you stick to the builder pattern, the simplest solution would be to modify the builder to accept Null values:
class Builder {
private var type1: Type1 = <default>
private var type2: Type2 = <defaul>
fun setType1(type: Type1?): Builder {
if (type != null) this.type1 = type
return this
}
fun setType2(type: Type2?): Builder {
if (type != null) this.type2 = type
return this
}
}
That way you can keep:
class1.newBuilder()
.setType1(var1)
.setType2(var2)
.build()
To communicate the fact that the values won't be set if they are Null, change the name of the setters to e.g. setTypeIfNotNull.
With Kotlin the builder pattern is kind of obsolete (unless you create a DSL) because you can use a constructor with default and named arguments instead, something like:
class Request(val type1: Type1 = <default>, val type2: Type2 = <default>)
Then you can do:
Request(
type1 = type1,
type2 = type2,
)
Now this doesn't cover the Null case but you can use this to accept null values:
companion object {
public operator fun invoke(type1: Type1, type2: Type2? = null) = Request(type1, type2 ?: <default value>)
}
The reason why there's a companion object instead of a constructor is explained here: Is there a way to use the default value on a non-optional parameter when null is passed?.
Suppose I have two methods:
private fun method1(a: A): A {
return a.copy(v1 = null)
}
private fun method2(a: A): A {
return a.copy(v2 = null)
}
Can I write something like:
private fun commonMethod(a: A, variableToChange: String): A {
return a.copy($variableToChange = null)
}
Another words, can I use a variable to refer to a named argument?
If I understand correctly what you are trying to archive I would recommend to pass a setter to the method e.g.
fun <A> changer (a: A, setter: (a: A) -> Unit ) {
// do stuff
setter(a)
}
Is this what you are looking for?
A possible solution for this problem (with usage of reflection) is:
inline fun <reified T : Any> copyValues(a: T, values: Map<String, Any?>): T {
val function = a::class.functions.first { it.name == "copy" }
val parameters = function.parameters
return function.callBy(
values.map { (parameterName, value) ->
parameters.first { it.name == parameterName } to value
}.toMap() + (parameters.first() to a)
) as T
}
This works with all data classes and all classes that have a custom copy function with the same semantics (as long as the parameter names are not erased while compiling). In the first step the function reference of the copy method is searched (KFunction<*>). This object has two importent properties. The parameters property and the callBy function.
With the callBy function you can execute all function references with a map for the parameters. This map must contain a reference to the receiver object.
The parameters propery contains a collection of KProperty. They are needed as keys for the callBy map. The name can be used to find the right KProperty. If a function as a parameter that is not given in the map it uses the default value if available or throws an exception.
Be aware that this solution requires the full reflection library and therefore only works with Kotlin-JVM. It also ignores typechecking for the parameters and can easily lead to runtime exceptions.
You can use it like:
data class Person (
val name: String,
val age: Int,
val foo: Boolean
)
fun main() {
var p = Person("Bob", 18, false)
println(p)
p = copyValues(p, mapOf(
"name" to "Max",
"age" to 35,
"foo" to true
))
println(p)
}
// Person(name=Name, age=15, foo=false)
// Person(name=Max, age=35, foo=true)
I am trying to find a solution for a nice kotlin data class solution. I have already this:
data class Object(
var classMember: Boolean,
var otherClassMember: Boolean,
var example: Int = 0) {
fun set(block: Object.() -> kotlin.Unit): Object {
val copiedObject = this.copy()
copiedObject.apply {
block()
}
return copiedObject
}
fun touch(block: Object.() -> kotlin.Unit): Object {
return this.set {
classMember = true
otherClassMember = false
block() }
}
}
val test = Object(true,true,1)
val changedTest = test.touch { example = 2 }
the result of this method is that the changedTest object has classMember = true, otherClassMember = false and example = 2
The problem with this solution is, the class properties are not immutable with var declaration. Does somebody have an idea how to optimize my methods to change var to val?
val says that a variable can't change it's value after initialization at the definition point. Kotlin's generated copy method does not modify an existing copy after construction: this method actually uses retrieved values from an object, replaces these values with ones that provided in copy method (if any), and after that just constructs a new object using these values.
So, it is not possible to perform such an optimization if you are going to change object's state after construction.
If I understood what you want correctly, you can do
data class Object(
val classMember: Boolean,
val otherClassMember: Boolean,
val example: Int = 0) {
fun touch(example: Int = this.example): Object {
return copy(
classMember = true,
otherClassMember = false,
example = example)
}
}
val test = Object(true,true,1)
val changedTest = test.touch(example = 2)
Though you need to repeat parameters other than classMember and otherClassMember but without reflection you can't do better.
is there any better way to write generic swap function in kotlin other than java way described in How to write a basic swap function in Java.
Is there any kotlin language feature which can make generic swap function more concise and intuitive?
No need a swap function in Kotlin at all. you can use the existing also function, for example:
var a = 1
var b = 2
a = b.also { b = a }
println(a) // print 2
println(b) // print 1
If you want to write some really scary code, you could have a function like this:
inline operator fun <T> T.invoke(dummy: () -> Unit): T {
dummy()
return this
}
That would allow you to write code like this
a = b { b = a }
Note that I do NOT recommend this. Just showing it's possible.
Edit: Thanks to #hotkey for his comment
I believe the code for swapping two variables is simple enough - not to try simplifying it any further.
The most elegant form of implementation IMHO is:
var a = 1
var b = 2
run { val temp = a; a = b; b = temp }
println(a) // print 2
println(b) // print 1
Benefits:
The intent is loud and clear. nobody would misunderstand this.
temp will not remain in the scope.
Kotlin encourages the use of immutable data when possible (such as using val instead of var). This greatly reduces the change for subtle bugs, since it's possible to reason more soundly about code if values don't change.
Swapping two values is very much the opposite of immutable data: Did I mean the value of a before or after the swap?
Consider rewriting your code in the following immutable way:
val a = 1
val b = 2
val (a2, b2) = b to a
This works by making use of destructuring declarations, along with the built-in to extension function that creates a Pair.
That is a good usage for with:
var a = 1
var b = 2
with(a) {
a = b
b = this
}
println(a) // 2
println(b) // 1
Very simple, fast and elegant solution:
var a = 1
var b = 2
val (b0, a0) = a swap b
a = a0
b = b0
infix fun <A> A.swap(second: A): Pair<A, A> = second to this
prefer a=b.apply {b=a} for swapping the elements.
If we want to perform some operation on the variable inside the lambda, then go for
a = b.also {someFun(it)}
If you're swapping array values in place, from a code readability perspective, it was helpful for me to add an extension function swapInPlace
fun <T> Array<T>.swapInPlace(i1: Int, i2: Int){
this[i1] = this[i2].also{ this[i2] = this[i1] }
}
fun main(){
val numbers = arrayOf(2, 1)
//This is easier for me to read...
numbers.swapInPlace(0, 1)
//Compared to this
numbers[0] = numbers[1].also{ numbers[1] = numbers[0] }
}
I have something interesting for all:
Why just numbers. We can swap anything with a generic class and a generic function
class Mutable<T>(var value: T) {
override fun toString() = value.toString()
/**
infix fun swapWith(other: Mutable<T>) {
value = other.value.also { other.value = value }
}
**/
}
fun <T> swap(num1: Mutable<T>, num2: Mutable<T>) {
num1.value = num2.value.also { num2.value = num1.value }
}
fun main() {
val num1 = Mutable(4)
val num2 = Mutable(6)
println("Before Swapping:-\n\tNumber#1 is: $num1\n\tNumber#2 is: $num2\n")
//calling way of class method is not like usual swap function
//num1 swapWith num2
//calling the actual swap function.
swap(num1, num2)
println("After Swapping:-\n\tNumber#1 is: $num1\n\tNumber#2 is: $num2\n")
}
class Mutable is a generic class here which can contain any type of data into it.
I overridden toString() method to directly accessing the value attribute by just calling the object.
fun swap is a true swap function for kotlin that gives you the call by reference's demo too.
operator swapWith also works as swap function, which is a part of Mutable class. I have commented that part because the calling way for the operator is not like the way we are used to with.
Output:
Before Swapping:-
Number#1 is: 4
Number#2 is: 6
After Swapping:-
Number#1 is: 6
Number#2 is: 4
I have different approach.
You can keep your two values in a Pair. Then you can do this:
fun <T> swap(pair: Pair<T, T>): Pair<T, T> {
return Pair(pair.second, pair.first)
}
and you use it like this:
var pairOfInts = Pair(1, 2)
println("first: ${pairOfInts.first}") // prints 1
println("second: ${pairOfInts.second}") // prints 2
pairOfInts = swap(pairOfInts)
println("first: ${pairOfInts.first}") //prints 2
println("second: ${pairOfInts.second}") //prints 1
In order to use Kotlin List you could create this kind of extension. It returns a copy of this list with elements at indices a and b swapped.
fun <T> List<T>.swap(a: Int, b: Int): List<T> = this
.toMutableList()
.also {
it[a] = this[b]
it[b] = this[a]
}
If you use an array, you can use this:
fun <T> Array<T>.swap(i: Int, j: Int) {
with(this[i]) {
this#swap[i] = this#swap[j]
this#swap[j] = this
}
}