开发者

Extending a trait and types

开发者 https://www.devze.com 2023-01-27 04:43 出处:网络
I would like to have a sealed trait which have a declared method that returns the actual class that extends the trait. Should I use an abstract type, a parameter type or

I would like to have a sealed trait which have a declared method that returns the actual class that extends the trait. Should I use an abstract type, a parameter type or is there any other nice way to solve this?

sealed trait Foo {
  type T
  def doit(other: T): T
}

or

sealed trait Foo[T] {
  def doit(other: T): T
}
开发者_如何学C

Note that T must be a subtype of Foo in this example. If I do it like this the type information feels too repeated:

case class Bar(name: String) extends Foo[Bar] {
  def doit(other: Bar): Bar = ...
}


They are mostly interchangeable. According to Odersky, the reason was mainly for completeness: That similarly to the fact that methods and fields (values) can be either abstract or passed as parameters, so can types.

It is better to use an abstract type when you intend to mix several traits that all use the same type name. With type parameters you need to explicitly pass the type to each

Here's an article explaining all of this: http://www.artima.com/weblogs/viewpost.jsp?thread=270195


You can cut down on the repetition somewhat by having your doit method return a factory function:

trait Foo[T] { 
   self: T =>
   def doit: T => T 
}

case class Bar(name: String) extends Foo[Bar] {
   // note: types omitted 
   def doit = { other => Bar(name + other.name) }
}

It's not possible to do the same with an abstract type:

trait Foo { 
   self: T => // won't compile because T isn't defined yet
   type T 
   def doit: T => T
}


You can write:

trait Foo[T] {
  self:T =>
  def doit(other: T): T
}

case class Bar(name: String) extends Foo[Bar] {
  def doit(other: Bar): Bar = ...
}

The difference to your example is that Bar can't be instantiated in any other way (e.g. case class Bar(name: String) extends Foo[String]).


trait Foo[A <: Foo[A]]

This trait can only be mixed in if A is a subtype of Foo[A] and the only type satisfying that is the class Foo is being mixed into. I saw this solution in the Mapper traits in Lift.


EDIT - Below is my original answer. Your comment indicates that you wish to return an arbitrary instance of a matching type but I don't really believe that this is in any way sensible. Suppose it were, via the T.type syntax:

trait T { def foo : T.type }

trait U extends T { def foo = new U } //must be a U

class W extends U

val w : W = (new W).foo //oh dear.

This is accomplishable via this.type:

scala> trait T {
 | def foo : this.type
 | }
defined trait T

scala> class W extends T {
 | def foo  = this
 | }
defined class W

scala> (new W).foo
res0: W = W@22652552

scala> res0.foo
res1: res0.type = W@22652552

And then also:

scala> ((new W) : T)
res4: T = W@45ea414e

scala> res4.foo.foo.foo
res5: res4.type = W@45ea414e
0

精彩评论

暂无评论...
验证码 换一张
取 消