Generic types in class definitions and subclasses in Kotlin - kotlin

I don't know the terminology of what I am trying to do, which makes asking the question difficult. Here is a minimal example:
abstract class Obj() {
abstract fun checkIssues() : List<String>
fun runRules(rules: List<Rule<Obj>>): List<String> {
return rules.mapNotNull {
it.check(this)
}
}
}
class Field(): Obj(){
companion object {
val rules = listOf(MissingResetRule())
}
override fun checkIssues(): List<String> = runRules(rules)
}
class MissingResetRule : Rule<Field>() {
override fun check(obj: Field): String? = ""
}
abstract class Rule<T: Obj> {
abstract fun check(obj: T): String?
}
The problem is that List<MissingResetRule> is not a subclass ofList<Rule<Obj>> or to be more precise, MissingRule is not a subclass of Rule<Obj>. This means runRules(rules) complains about the types being wrong. But Field is a subclass of Obj so I would have thought that Rule<Field> would be a subclass of Rule<Obj>. I can't seem to figure out how to tell the compiler that everything is valid here.
I have read the documentation here: https://kotlinlang.org/docs/reference/generics.html many times and can't understand how it applies to the code above.

As far as I understand your question, you try to have a function runRules that exactly get the rules of the corresponding type. Then make this function generic:
abstract class Obj() {
abstract fun checkIssues() : List<String>
}
fun <T : Obj> T.runRules(rules: List<Rule<T>>): List<String> {
return rules.mapNotNull {
it.check(this)
}
}
class Field(): Obj(){
companion object {
val rules = listOf(MissingResetRule())
}
override fun checkIssues(): List<String> = runRules(rules)
}
class MissingResetRule : Rule<Field>() {
override fun check(obj: Field): String? = ""
}
abstract class Rule<T: Obj> {
abstract fun check(obj: T): String?
}

Related

Inherit Companion Obejct in Children - Kotlin

I've read that static methods cannot overridden in Kotlin, so I'm not sure if this is possible, but not being able to do so would result in a lot of repetitious code. Is there any way to achieve the same behavior while moving the companion object into the Parent? Here is what I have so far
Parent.kt
abstract class Parent {
protected val TAG = this::class.java.simpleName
}
Brother.kt
class Brother: Parent() {
companion object {
#Volatile private var instance: Brother? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: Brother().also { instance = it }
}
}
}
Sister.kt
class Sister: Parent() {
companion object {
#Volatile private var instance: Sister? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: Sister().also { instance = it }
}
}
}
main()
fun main() {
println("Hello, ${Brother.getInstance().TAG}")
println("Hello, ${Sister.getInstance().TAG}")
}
Console Output:
Hello, Brother Hello, Sister
Maybe this will work for what you're trying to do.
You can create a superclass for objects that do this pattern:
open class SingletonAccessor<T: Any> (private val constructor: () -> T){
#Volatile private var instance: T? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: constructor().also { instance = it }
}
}
And then inherit it from your implementation class companion objects:
class Brother private constructor(): Parent() {
companion object: SingletonAccessor<Brother>(::Brother)
}
class Sister private constructor(): Parent() {
companion object: SingletonAccessor<Sister>(::Sister)
}
This pattern isn't much different from simply making Brother and Sister objects, since they have no constructor parameters, but maybe this is just a simplified example.
Based on #Tenfour04's answer, I've come up with an alternate approach, which incorporates the SingletonAccessor into the Parent
abstract class Parent<T>(private val constructor: () -> T) {
#Volatile private var instance: T? = null
protected val TAG = this::class.java.simpleName
fun getInstance() = instance ?: synchronized(this) {
instance ?: constructor().also { instance = it }
}
}
The implementation in the children is the same as before.
Let me know if this answer can be improved further. In particular, I would like to do in the class declaration class Parent<T: Parent>, but that doesn't compile. Is there a way to limit the type parameter to itself and its children?

How to have multiple upper bounds for the generic type parameter of a function?

There are an abstract class Base and a interface BaseAction. In my project, A and B both implements Base, BaseAction.
abstract class Base {
abstract fun doAction()
}
interface BaseAction {
}
class A: Base(), BaseAction {
override fun doAction() {
}
}
class B: Base(), BaseAction {
override fun doAction() {
}
}
Now I define an abstract class AbstractCommand and there is a function:
abstract class AbstractCommand {
abstract fun <T> getActions(): List<T> where T: Base, T: BaseAction
fun doSomething() {
// Here A and B both are Ok for me.
getActions<>()
}
}
As you see, there are two upper bounds: Base and BaseAction. In function doSomething, I want to invoke getActions,in that List,class A and B both are ok for me.
My question is how I can invoke getActions.
EDIT
Yes I can define T in class AbstractCommand, but if so, AbstractCommand only return List<A> or List<B>. However List is expected.
EDIT
It's my fault for my description. I must explain more about my question and explain it's not suitable to let A and B extends same parent directly.
In fact, my question is more complex as describe above. Let me hold my full example.
abstract class Base {
abstract fun doAction()
}
interface BuyGiftAction {
abstract fun buyGift()
}
abstract class A: Base(){
abstract fun doActionA()
}
abstract class B: Base() {
abstract fun doActionB()
}
As you see, A and B both extends Base class and define new abstract function. Now child of A want to implement BuyGiftAction and child of B does so.
class ChildA: A(), BuyGiftAction {
override fun doActionA() {
}
override fun doAction() {
}
override fun buyGift() {
}
}
class ChildB: B(), BuyGiftAction {
override fun doActionB() {
}
override fun doAction() {
}
override fun buyGift() {
}
}
Obviously it's very not suitable to let A and B both extend a class named Parent who extends Base and implements BuyGiftAction.
abstract class AbstractCommand {
abstract fun <T> getActionArray(): Array<T> where T: Base, T: BuyGiftAction
fun doSomething() {
// Here ChildA and ChildB both are Ok for me.
getActionArray<>()
}
}
Try it here!
In order for doSomething to use the same T as getActions, you need to make T a generic type parameter of AbstractCommand:
abstract class AbstractCommand<T> where T: Base, T: BaseAction {
abstract fun getActions(): List<T>
fun doSomething() {
getActions()
}
}
The way you defined getActions() currently means you return a list of a single type T which needs to both extend Base and implement BaseAction. So either only As in your list, or only Bs, but you cannot mix As and Bs here.
If you want heterogeneous types in your list, you will have to make getActions() return something else than T, something like a single base class.
I think the easiest way would be to make the base class Base implement BaseAction, or to create an intermediate class (e.g. Action) that both extends Base and implement BaseAction, and which A and B could derive from.
abstract class Action : Base(), BaseAction
class A: Action() {
override fun doAction() { ... }
}
class B: Action() {
override fun doAction() { ... }
}
abstract class AbstractCommand {
abstract fun getActions(): List<Action>
fun doSomething() {
// Here A and B both are Ok for me.
getActions()
}
}

Is it possible to verify at compile time whether the required function is called for the Factory Class in Kotlin?

class ModelFactory {
fun setA() : ModelFactory {
// blabla...
}
fun setB() : ModelFactory {
// blabla...
}
fun setC() : ModelFactory {
// blabla...
}
fun build() : Model {
// An error occurs if any of setA, setB, and setC is not called.
}
}
//example
fun successTest() {
ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}
fun failTest() {
ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}
It's awkward grammatically, but I think it's been expressed what I want.
I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.
If possible, I think I should use annotations. But is this really possible at compile time?
With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.
If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.
class Model(val a: String = "", val b: String = "", val c: String = "")
class StepBuilder {
companion object {
fun builder(): AStep = Steps()
}
interface AStep {
fun setA(a: String): BStep
}
interface BStep {
fun setB(b: String): CStep
}
interface CStep {
fun setC(c: String): BuildStep
}
interface BuildStep {
//fun setOptionalField(x: String): BuildStep
fun build(): Model
}
class Steps : AStep, BStep, CStep, BuildStep {
private lateinit var a: String
private lateinit var b: String
private lateinit var c: String
override fun setA(a: String): BStep {
this.a = a
return this
}
override fun setB(b: String): CStep {
this.b = b
return this
}
override fun setC(c: String): BuildStep {
this.c = c
return this
}
override fun build() = Model(a, b , c)
}
}
fun main() {
// cannot build until you call all three setters
val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
}

How to return T in Kotlin?

I want to return T in the function.
I have an interface class IRepository.kt
interface IRepository
{
fun <T>Save(model:T)
fun <T>Delete(model:T)
fun <T>Get(id:Long):T
}
I want to implement in Repolmpl.kt
class Repolmpl:IRepository
{
override fun <T>Delete(model:T)
{
println("$model : Save}")
}
override fun <T>Get(id:Long):T
{
return T //ERROR here I want to return T...
}
override fun <T> Save(model: T)
{
println("$model : Delete")
}
}
I saw some similar questions online but I just can't find the right solution.
A generic type T is basically just a template. You cannot return it but have to replace it with an actual type first. Make the interface itself generic, not its methods. When implementing, specify T:
interface IRepository<T> {
fun save(model: T)
fun delete(model: T)
fun get(id: Long): T
}
class Repolmpl: IRepository<String>
{
override fun delete(model: String) {}
override fun get(id: Long): String {}
override fun save(model: String) {}
}
You cannot just return T. T is type here, and it is like return String.
You have to return instance of T. So, sth like:
class Repo {
val data = mapOf<Long, Any>()
// ...
fun <T> get(id: Long): T {
return data[id] as T // Get data from somewhere and then cast it to expected type
}
}

Invoking methods on interfaces with generics

The following is a very simple illustration of what I'm trying to do:
interface Event {
fun value(): Int
}
class Event1: Event {
override fun value() = 1
}
class Event2: Event {
override fun value() = 2
}
interface EventConsumer<T> where T: Event {
fun consume(event: T)
}
class Event1Consumer: EventConsumer<Event1> {
override fun consume(event: Event1) {
println(event.value())
}
}
class Event2Consumer: EventConsumer<Event2> {
override fun consume(event: Event2) {
println(event.value())
}
}
class EventManager {
private val consumers: Map<KClass<*>, EventConsumer<*>> = mapOf(
Event1::class to Event1Consumer(),
Event2::class to Event2Consumer()
)
fun consume(event: Event) {
val consumer = consumers[event::class]
consumer?.consume(event)
}
}
The final method call (consumer.consume()) is giving me a compiler error
Out-projected type 'EventConsumer<*>?' prohibits the use of 'public
abstract fun consume(event: T): Unit defined in EventConsumer'
I know that Kotlin is a lot more strict about generics than Java which is probably why it doesn't work, but how would I implement something like this properly?
Since you are building the consumers map, it would be safe to make an unchecked cast to the correct generic EventConsumer type:
fun <T: Event> consume(event: T) {
val consumer = consumers[event::class] as? EventConsumer<T>
consumer?.consume(event)
}