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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With