Reputation: 561
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
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
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
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