Frage

If I have a type alias definition in a class, can I compare it during run time with a statically known type or other type alias? Consider:

type ConsArguments = (Option[Long], String, Option[String], Iterable[Input])
trait Type {
    val name :String
    type Value
    def apply(id :Option[Long], name :String, label :Option[String], inputs :Iterable[Input]=Iterable()) :Statistic
}
class BaseType[V :TypeTag](val name :String, constructor :((ConsArguments)) => Statistic {type Value=V}) extends Type{
    type Value = V
    def apply(id :Option[Long], name :String, label :Option[String], inputs :Iterable[Input]=Iterable()) :Statistic{type Value=V} =
        constructor((id, name, label, SortedSet[Input]()(Input.nameOrdering)++inputs))
}
val LongValued = new BaseType[Long]("long", (LongStatistic.apply _).tupled)
val lv :Type = LongValued
println("type of LongValued: "+universe.typeOf[LongValued.Value]+" is Long? "+(universe.typeOf[LongValued.Value]=:=universe.typeOf[Long]))
println("type of lv: "+universe.typeOf[lv.Value]+" is Long? "+(universe.typeOf[lv.Value]=:=universe.typeOf[Long]))

The first comparison is true, the second false. Can I somehow fix it? Generally, I'll have more instances of 'Type' serving as constructors for classes from my domain model and would like to iterate over a collection of those and choose a matching one.

War es hilfreich?

Lösung 2

Ok, I figured it out. The change is really simple:

type ConsArguments = (Option[Long], String, Option[String], Iterable[Input])
trait Type {
    val name :String
    type Value
    val tpe :universe.Type
    def apply(id :Option[Long], name :String, label :Option[String], inputs :Iterable[Input]=Iterable()) :Statistic
}
class BaseType[V :TypeTag](val name :String, constructor :((ConsArguments)) => Statistic {type Value=V}) extends Type{
    type Value = V
    val tpe = typeOf[V]
    def apply(id :Option[Long], name :String, label :Option[String], inputs :Iterable[Input]=Iterable()) :Statistic{type Value=V} =
        constructor((id, name, label, SortedSet[Input]()(Input.nameOrdering)++inputs))
}
val LongValued = new BaseType[Long]("long", (LongStatistic.apply _).tupled)
val lv :Type = LongValued
println("type of lv: "+lv.tpe+" is Long? "+(lv.tpe=:=universe.typeOf[Long]))

Still, a bit annoying to have double (type ... and val _ :Type) declaration all over the code.

Andere Tipps

The problem is that when you statically type lv as Type you throw away the (static) information about the Value type member. If you let the type be inferred (or add { type Value = Long } to the explicit type annotation), you'll be fine.

As soon as you make a collection of instances of Type with different values for Value, though, you're out of luck again, since the inferred type of the collection will be something like List[Type { type Value >: Long with Whatever }], which is essentially just the same as List[Type]. Thanks to type erasure (and I'm not being sarcastic—type erasure really is doing the right thing here), you're not going to be able to use pattern matching or the traditional Java reflection tools to pick items out of the collection based on their Value type member.

So the short answer is "don't do this". You'd be going into a land where static types can't help you, and there's almost certainly a better way to structure your program. If you absolutely insist on sticking to this approach, though, you could use type tags. The following is a slightly simplified example:

scala> import scala.reflect.runtime.universe.{ TypeTag, typeOf }
import scala.reflect.runtime.universe.{TypeTag, typeOf}

scala> trait Foo { type Value; implicit def vTag: TypeTag[Value] }
defined trait Foo

scala> class Bar[V](implicit val vTag: TypeTag[V]) extends Foo { type Value = V }
defined class Bar

scala> val xs = List(new Bar[Int], new Bar[String], new Bar[Symbol])
xs: List[Bar[_ >: Symbol with String with Int]] = ...

scala> xs.filter(_.vTag.tpe =:= typeOf[Symbol])
res0: List[Bar[_ >: Symbol with String with Int]] = List(Bar@68e2cd6f)

This is really unpleasant, though—we're just passing around information about the types of our values as runtime values themselves. The new reflection API makes this fairly clean syntactically, but that doesn't mean it's a good idea.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top