Lena
Lena

Reputation: 561

How to switch between two types in Kotlin

It might be a really simple question but I don't know how to deal with it. I have an open class called "User". This class gets -among others- a propriety named "role".

This role can either be A or B - which are personal defined classes

How can I set it up in my Kotlin User class, please?

open class User (
    var email : String = "",
    var role : ???
)

class A : User() {
    var name : String = ""
}

class B : User() {
    var color : String = ""
}

Upvotes: 1

Views: 611

Answers (3)

cactustictacs
cactustictacs

Reputation: 19544

An interface might work, but your A and B classes have different properties, so there isn't really anything in common to put in a shared interface. You'd need separate ones, RoleA and RoleB, so you can do things like if (user is RoleB) print(user.color).

You can already do that with your code (if (user is B) print(user.color)) but it's a composition vs inheritance thing - it might be better to just subclass user like you're doing, or it might be better to add interfaces to stack different behaviours - depends what you're doing and which is least complicated!


If you do want to keep role as a property instead of making it a type that User has (so if (user.role is RoleA) rather than if (user is RoleA)) you might want to look at sealed classes:

sealed class Role {
    data class A(var name: String = "") : Role()
    data class B(var color: String = "") : Role()
}

then you can have

class User {
    var email: String = "",
    val role: Role
}

The sealed class basically acts as a group of types - the classes and objects inside can all be completely different, they don't need to share any common methods and properties or anything. But because you're defining the Role class as those things (and only those things), you can check which specific type a Role is, and you can do exhaustive pattern matching too:

when(role) {
    is Role.A -> print(role.name)
    is Role.B -> print(role.color)
}

and it knows you've covered all the possible cases. So you can make each Role class whatever you want, and treat each one accordingly. It might be a bit neater than using and implementing interfaces!

Upvotes: 2

cutiko
cutiko

Reputation: 10517

This is what its called a generic or a parametrized data type, I think the best for the implementation is to leverage abstract classes for the parent and then for the child data classes

abstract class User<RoleType> (
    var email : String = "",
    abstract var role : RoleType
)

data class A(override var role: SomeClassOfYou) : User<SomeClassOfYou>()

data class B(override var role: AnotherClassOfYou) : User<AnotherClassOfYou>()

The problem with the above is you are basically saying, that your RoleType is any which ranges from Int to whatever class exists, so it is better to have another type to delimit it, an interface by example.

interface RoleType {
    //add your methods
}

Then that way we can constraint the type to RoleType interface and any implementation will be valid

abstract class User<Type : RoleType> (
    var email : String = "",
    abstract var role : RoleType
)

//Assuming there is a Foreman class that implements RoleType
data class A(override var role: Foreman) : User<Foreman>()

//... the other class

Another thing you should be aware if that the role is just a category and the interface implementing it would have no methods, then using an enum is better

enum class UserRole {
    Foreman,
    Worker
}

abstract class User<Type : RoleType> (
    var email : String = "",
    abstract var role : UserRole
)

And the last thing, consider using a sealed class in conjunction with enum well:

sealed class User(
    var email : String = "",
    open var role : UserRole
) {

    //Making the child classes inner can use the parent class as a domain naming
    data class Foreman(val email: String, override var role: UserRole) : User(email, role)

}


Upvotes: 2

Suraj Bahadur
Suraj Bahadur

Reputation: 3928

You can use the Any keyword to hold the class object reference and I also recommend you to use the interface suggested by @deHaar

open class User (
        var email : String = "",
        var role : Any
    )

    class A : User("",A()) {
        var name : String = ""
    }

    class B : User("",B()) {
        var color : String = ""
    }

Upvotes: 1

Related Questions