I'm trying to write some generic builders for my User class hierarchy. I have a trait, UserBuilder and each "with" method in the trait has to return the same type as the current class. So if I'm inside the ComplexUserBuilder, the withId method should return a ComplexUserBuilder and not a UserBuilder.
But I'm getting
illegal cyclic reference involving type T
Is there a way to workaround this?
Here is my code:
trait UserBuilder[T >: UserBuilder[T]] {
var id: String = ""
def withId(id: String): T = {
this.id = id
return this
}
}
class ComplexUserBuilder extends UserBuilder[ComplexUserBuilder] {
var username: String = ""
def withUsername(username: String): ComplexUserBuilder = {
this.username = username
return this
}
def build = new ComplexUser(id, username)
}
By the way, if I replace trait UserBuilder[T >: UserBuilder[T]]
with trait UserBuilder[T >: UserBuilder[_]]
I get:
type arguments [model.ComplexUserBuilder] do not conform to trait UserBuilder's type parameter bounds [T >: model.UserBuilder[_]]
Update:
trait UserBuilder[T >: UserBuilder[T]]
should be (as GClaramunt suggested)
trait UserBuilder[T <: UserBuilder[T]]
but now there is an ugly cast as the return type
To make your UserBuilder
subclasses have a reference to their own type, you should declare the type of this
(and make them sub-types, not super-types, of UserBuilder
):
trait UserBuilder[T <: UserBuilder[T]] { this: T =>
...
}
Dan has solved your problem pretty clear. What I want to say is, you even don't need the generic T at all, by using the this.type feature of scala.
trait UserBuilder {
var id: String = ""
def withId(id: String): this.type = {
this.id = id
return this
}
}
class ComplexUserBuilder extends UserBuilder {
var username: String = ""
def withUsername(username: String): this.type = {
this.username = username
return this
}
def build = new ComplexUser(id, username)
}
And, if you want to add the build method to the UserBuilder trait, you may want to add a generic type U to constraint the user type, like
trait UserBuilder[U <: { def id: String }] {
var id: String = ""
def withId(id: String): this.type = {
this.id = id
return this
}
def build: U
}
class ComplexUserBuilder extends UserBuilder[ComplexUser] {
var username: String = ""
def withUsername(username: String): this.type = {
this.username = username
return this
}
def build = new ComplexUser(id, username)
}
I'm not sure what are you trying to achieve, but you have the upper type bound restriction backwards should be "<:" instead of ":>"
:> is a lower type bound restriction, meaning, T must be a super type of UserBuilder, you want a subtype (so you can extend UserBuilder )
trait UserBuilder[T <: UserBuilder[T]] {
self: T =>
var id: String = ""
def withId(id: String): T = {
this.id = id
this
}
}
class ComplexUserBuilder extends UserBuilder[ComplexUserBuilder] {
var username: String = ""
def withUsername(username: String): ComplexUserBuilder = {
this.username = username
this
}
//def build = new ComplexUser(id, username)
}
Also, you don't need "return", (almost) everything in Scala is an expression that returns a value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With