Kotlin parent logic to run after child's constructor - kotlin

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

Related

Is there a way to define an implicit ctor for a Kotlin class?

as the title says it, is there any way to define a constructor or convertion for a class/object that can be called implicitly like this?
class Person {
val name: String
val mood: Mood = Mood.Happy
fun toString(): String = "$name is $mood"
}
fun main() {
// Calls the magic implicit ctor
val steve: Person = "Steve"
// Prints "Steve is happy"
println("$steve")
}

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 the set of abstract properties on sealed sub classes (in 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") }

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.

Use a class from a list of generic interface

I am trying to implement a QueryBus. Basically, I want to register a list of QueryHandlers. Each QueryHandler implements a handle method defined by an interface. Each QueryHandler is associated to a Query. I want to be able to retrieve a QueryHandler using the Query and call handle on it.
The thing is the handle has to be generic because each QueryHandler handles a Query differently. They all take a dedicated Query and may return whatever they want.
interface Query<R>
interface QueryHandler<R, Q : Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
class ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
class GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = mapOf(
with(ListBooksQueryHandler()) {this.listenTo() to this},
with(GetBookQueryHandler()) {this.listenTo() to this}
)
val command = ListBooksQuery()
val result = queryHandlers[command::class.toString()].handle(command)
// Should print the list of BookDto
print(result)
}
I don't even know if its possible, to be honest.
UPDATE 1:
I changed the usage example in the main to show what I am really trying to do. The List was for (bad?) demonstration purpose. I want to store the QueryHandlers and retrieve them from a map.
Additional resources:
Here is what I really want to do:
https://gist.github.com/ValentinTrinque/76b7a32221884a46e657090b9ee60193
UPDATE I've read your gist and tried to come up with a solution that will provide a clean interface to the user of the QueryBusMiddleware.
Note that I used objects instead of classes for the QueryHandler implementations, which felt more natural to me (since there is only one possible entry in the map for each Query implementation).
interface Query<R>
interface QueryHandler<R, Q: Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
object ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
object GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = listOf(
ListBooksQueryHandler,
GetBookQueryHandler
)
val dispatcher: QueryBusMiddleware = QueryDispatcherMiddleware(queryHandlers)
// Calling query bus
val query = ListBooksQuery()
// Result should be List<BookDto>
val result = dispatcher.dispatch(query)
print(result)
}
interface QueryBusMiddleware {
fun <R, Q : Query<R>> dispatch(query: Q): R
}
class QueryDispatcherMiddleware constructor(handlers: List<QueryHandler<*, *>>) : QueryBusMiddleware {
private val handlers = HashMap<String, QueryHandler<*, *>>()
init {
handlers.forEach { handler -> this.handlers[handler.listenTo()] = handler }
}
override fun <R, Q : Query<R>> dispatch(query: Q): R {
val queryClass = query::class.toString()
val handler = handlers[queryClass] ?: throw Exception("No handler listen to the query: $queryClass")
return handler::class.members.find { it.name == "handle" }!!.call(handler, query) as R
}
}