0

I am so confused, I've been searching how kotlin generics work, but I don't get it at all.

Basically, I have BaseAnimalDetail class, which is being used with some classes extending Animal class. At the moment, there are a lot of when in the BaseAnimalDetail class, so my goal is to avoid all those when, just extending BaseAnimalDetail whenever I need, for each class that extends Animal`

open class BaseAnimalDetail<T : Animal?> : LinearLayout{
    private var animal: T? = null
    
    fun setAnimal(animal:T){
        this.animal = animal
    }

    private fun play() {
        when(animal){
           is Horse -> playWithHorse...
           is Dog -> playWithDog...
           is Cat -> playWithCat...
        }
        animal?.let { it.play() } //this is not working but animal?.play works fine, WHY?
    }
    ...More code
}

As you can see, when is super ugly, so I would like to make BaseAnimalDetail abstract and implement it like I said.

Code above is just an example, but it would help me to understand how generics work in Kotlin. It's mandatory for me to have the T var in the base class

Furthermore, WHY animal?.let { it.play() } does not compile?

1
  • Can you post your Animal class? Commented Oct 6, 2020 at 14:17

3 Answers 3

2

In the case you're describing, you want each type of Animal to have its own BaseAnimalDetail subclass to go with it. So you should make BaseAnimalDetail an abstract class, and then you can create subclasses for each Animal.

By the way, since Kotlin has properties, it is redundant to make animal private and have a setter function.

abstract class BaseAnimalDetail<T : Animal> {
    var animal: T? = null
    abstract fun play()
}

class CatDetail: BaseAnimalDetail<Cat>(){
    override fun play() {
        println("toss yarn to $animal")
    }
}

But if all these different functions are different for each type of Animal, it sounds to me like you shouldn't have a separate BaseAnimalDetail class at all, and its functionality should just be in the various Animal classes. Alternatively, if there are certain behaviors that some animals share, you can create various behavior classes that can be added to the Animal class (composition design strategty).

Sign up to request clarification or add additional context in comments.

4 Comments

I don't understand why you created that constructor. I have a setAnimal method to set it when I need. And it's mandatory for me to have the var animal T to use it from BaseAnimalDetail when needed
The property doesn't have to be in the constructor (that's just for convenience), but setAnimal is completely redundant in Kotlin because animal is a property. It has an implied getter and setter with a backing field. You can customize the setter at the declaration site later without changing any signatures, just like with a Java setter.
I updated it to pull the property out of the constructor. It's not really relevant to what you were asking, though, is it?
It just occurred to me maybe you weren't aware that properties in the constructor are just as valid as properties outside the constructor. They are not private to the constructor, but are available to use for the lifetime of the object, just like other properties. Putting them in the constructor makes it convenient to set them while you instantiate the class.
0

Not sure if understand u correctly, but what u could do is as u said - transform your BaseAnimalDetail to abstract class with abstract fun fun play() and let Animal class to extend BaseAnimalDetail or let ur Horse, Dog extend it directly.

abstract class BaseAnimalDetail {
    abstract fun play()
}

open class Animal : BaseAnimalDetail() {
    override fun play() = Unit
}

class Horse : Animal() {

    override fun play() {
        playWithHorse()
    }

    private fun playWithHorse() {}
}

class Dog : BaseAnimalDetail() {

    override fun play() {
        playWithDog()
    }

    private fun playWithDog() {}
}

Also "whenever I need" could be provided by an interface since our goal is to provide a function and there is no deeper logic there.

5 Comments

It's mandatory for me to have the var animal T to use it from BaseAnimalDetail when needed, furthermore, do you know why animal?.let { it.play() } does not compile? thanks
What is the error for animal?.let { it.play() }? Looks like it should work fine, although it's just a verbose way of writing animal?.play().
Type inference failed. Expected type mismatch: required: Animal found: T!
I think you've removed too much context of what's going on around it for us to see the problem. What you have above works fine as is: pl.kotl.in/hvAe2OnNy
This error would occur if the play function has a return type.
0

Not sure why do you need generics for this, here is my implementation

abstract class Animal {
    abstract fun play()
}

class Horse : Animal() {
    override fun play() {
        playWithHorse()
    }
    private fun playWithHorse() {}
}

class Dog : Animal() {
    override fun play() {
        playWithDog()
    }
    private fun playWithDog() {}
}


open class BaseAnimalDetail(var animal: Animal?=null) {
    private fun play() {
       animal?.play()
    }
}

class HorseDetail(var horse: Horse? = null): BaseAnimalDetail(horse){

}

You can remove play func from BaseAnimalDetail, instead directly call horse?.play() too.

3 Comments

My code is just an example of something more complex I need, I need to have a variable in BaseAnimalDetail (not in the constructor). Thanks
It is default argument so you don't need to pass the value in constructor.
Generics are needed if you want to retrieve animal and have it automatically treated as the known subtype by the compiler.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.