Access the set of abstract properties on sealed sub classes (in kotlin) - kotlin

I'm got a situation where I have a common property that must be defined on each of the subclasses of a sealed class.
I'd like the ability to be able to access the set/list of these values without 'duplicating' the list (by hard coding it)
Hopefully the below code conveys what I mean
sealed class S {
companion object {
// want to avoid typing: listOf("these", "values", please")
// instead grab it from the classes themselves
val properties = S::class.sealedSubclasses.map { /* What to do here? */ }
}
abstract val property: String
}
class A(val d: String) : S() {
override val property: String = "these"
}
class B(val e: String) : S() {
override val property: String = "values"
}
class C(val f: String) : S() {
override val property: String = "please"
}
I'm aware of fun <T : Any> KClass<T>.createInstance(): T from kotlin.reflect.full, but my constructors have non optional parameters.

You can create a createInstance(vararg) extension function for that:
fun <T : Any> KClass<T>.createInstance(vararg args: Any): T =
java.constructors.first().newInstance(*args) as T
S::class.sealedSubclasses.map { it.createInstance("the string") }

Related

Uniformly delegated properties in kotlin

When you have a backing object that you would like to wrap and delegate all properties with the same delegate logic, is there some way to generically describe a class implementation with all the properties delegated the same way? Take the example below:
class Subject<T, U>(
private val prop : KMutableProperty0<U>
) {
operator fun getValue(caller: T, prop: KProperty<*>) : U = this.prop.get()
operator fun setValue(caller: T, prop: KProperty<*>, value : U) {
// dostuff
this.prop.set(value)
}
}
interface Test {
val prop1 : String
val prop2 : String
}
#Serializable
class TestImpl(
override var prop1: String,
override var prop2: String
) : Test
// I don't want to have to write this boilerplate every time I need to delegate logic from a serializeable data object
class Wrapper( val test : TestImpl) : Test {
override var prop1 by Subject(testImpl::prop1)
override var prop2 by Subject(testImpl::prop2)
}
Is there any language feature that would let me write something like this:
class GenericWrapper<T , U : T>(val impl : U) : T /* can't do this obviously */{
// for each property on T, overwrite the property and delegate accordingly
}
The only thing I can think of at this point is writing my own compiler plugin, but is this the only option?

Kotlin parent logic to run after child's constructor

I got the following classes:
class B() : A {
override val p1: String = ...
override fun p2() : String { ... }
}
abstract class A() {
abstract val p1: String
abstract fun p2() : String
fun p3() : String = p1.plus("-").plus(p2())
init {
var p4 = p3()
}
}
I want p4 to be set at declaration. the issue is that when A() constructor is called p1 & p2() are not set yet, so p3() contains nulls. is there a way to solve it? like to run some logic after both constructors are called?
The function inheritance won't have any problem, as functions resolve statically.
Properties on the other hand, initialize after init block of super class has been executed.
You can simply fix this by moving the property p1 to the A's constructor instead, and add that to the constructor argument when extending the class:
class B : A(p1 = "...") { /* `p1 = ` is optional */
override fun p2(): String { return "asdf" }
}
abstract class A(val p1: String) {
abstract fun p2(): String
fun p3() : String = p1.plus("-").plus(p2())
init {
var p4 = p3()
println(p4) // prints: `...-asdf`
}
}
Easiest way is probably to just make it a lazy property that gets computed when it's first accessed (after construction)
abstract class A() {
abstract val p1: String
abstract fun p2() : String
fun p3() : String = p1.plus("-").plus(p2())
var p4: String by lazy { p3() }
}
If you actually want to use p4 in the init block though, i.e. you're doing something with it as part of the construction process, and you just want it to run after B has been constructed and initialised, I don't think there's any way around that. You might want to do some kind of getInstance approach instead
class B private constructor () : A() {
override val p1: String = "P1"
override fun p2() : String { return "P2" }
companion object {
fun newInstance() = B().apply{ initialise() }
}
}
abstract class A() {
abstract val p1: String
abstract fun p2() : String
fun p3() : String = p1.plus("-").plus(p2())
fun initialise() {
var p4 = p3()
print(p4)
}
}
but honestly the best way might just be to pass p1 and p2 into A as parameters, especially if it's a data class anyway

Kotlin generics with in produces Type mismatch when compiling

I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}

Access properties of a subclass of the declared object type

I have the following abstract class:
abstract class AbstractBook {
abstract val type: String
abstract val privateData: Any
abstract val publicData: Any
}
and the following class which inherits the AbstactBook class:
data class FantasyBook (
override val type: String = "FANTASY",
override val privateData: FantasyBookPrivateData,
override val publicData: FantasyBookPublicData
) : AbstractBook()
And then there is this class which should include data from any type of AbstractBook:
data class BookState(
val owner: String,
val bookData: AbstractBook,
val status: String
)
If I have an instance of BookState, how do I check which type of Book it is and then access the according FantasyBookPrivateData, and FantasyBookPublicData variables?
I hope I described my issue well & thanks in advance for any help!
What you describe is a sealed class:
sealed class Book<T, K> {
abstract val type: String
abstract val privateData: T
abstract val publicData: K
data class FantasyBook(
override val type: String = "FANTASY",
override val privateData: String,
override val publicData: Int) : Book<String, Int>()
}
and in your data class you can do pattern matching like this:
data class BookState(
val owner: String,
val bookData: Book<out Any, out Any>,
val status: String) {
init {
when(bookData) {
is Book.FantasyBook -> {
val privateData: String = bookData.privateData
}
}
}
}
to access your data in a type-safe manner. This solution also makes type redundant since you have that information in the class itself.
I agree with #Marko Topolnik that this seems like a code smell, so you might want to rethink your design.
interface AbstractBook<T , U> {
val privateData: T
val publicData: U
}
data class FantasyBook (
override val privateData: FantasyBookPrivateData,
override val publicData: FantasyBookPublicData
) : AbstractBook<FantasyBookPrivateData , FantasyBookPublicData>
data class BookState(
val owner: String,
val bookData: AbstractBook<*, *>,
val status: String
)
if(bookState.bookData is FantasyBook) {
// Do stuff
}
Creating a type variable is a weak type language writing style. You should use generic class.

Access delegate object from a method

Consider the following Kotlin code:
class Derived(n1: String, n2:String) : Teacher by TeacherImp(),Person by PersonImpl(n1, n2) {
// desire to call method of PersonImp object... possible??
}
Is there any way to access the delegate object instances?
Consider if the derived class wants to access a method of a delegate.
You can save the delegate(s) into private immutable property(s) - for example:
interface Teacher {
fun sayHelloTeacher() = println("Teacher hello")
}
interface Person {
fun sayHelloPerson() = println("Person hello")
}
class TeacherImp : Teacher {
fun sayHelloTeacherImp() = println("TeacherImp hello")
}
class PersonImp(val n1: String, val n2: String) : Person {
fun sayHelloPersonImp() = println("PersonImp hello $n1 $n2")
}
class Derived private constructor(private val t: TeacherImp, private val p: PersonImp) :
Teacher by t, Person by p {
constructor(n1: String, n2: String) : this(TeacherImp(), PersonImp(n1, n2))
init {
sayHelloPerson()
sayHelloTeacher()
t.sayHelloTeacherImp()
p.sayHelloPersonImp()
}
}
fun main(args: Array<String>) {
Derived("first", "second")
}
With this implementation the only public constructor is the same as the original, and which calls the private constructor that stores the actual objects.
Note: With reflection it may possible to access them without the extra constructor, but I think this is a straightforward solution to your problem.