首先Data.Aux[T] forSome { type T <: Type}可以写成Data.Aux[_]也就是Data
implicitly[(Data.Aux[T] forSome { type T <: Type}) =:= Data] // compiles
implicitly[Data =:= (Data.Aux[T] forSome { type T <: Type})] // compiles
如果你把println 放在handleMainType2 里面
import scala.reflect.runtime.universe.{Type, TypeTag, typeOf}
def getType[A: TypeTag](a: A): Type = typeOf[A]
def handleMainType2(main: Main): Unit =
println(getType(main.data) + "=" + showRaw(getType(main.data)))
然后
handleMainType2(Main(Data1(1)))
handleMainType2(Main(Data2("a")))
将打印以下内容之一
(取决于您如何定义 Main 参数的类型:Data.Aux[T] forSome { type T <: Type}、Data.Aux[_] 或 Data)
App.Data{type T = T}=RefinedType(List(TypeRef(ThisType(App), App.Data, List())), Scope(TypeName("T")))
App.Data{type T = _$1}=RefinedType(List(TypeRef(ThisType(App), App.Data, List())), Scope(TypeName("T")))
App.Data=TypeRef(ThisType(App), App.Data, List())
两次。所以内部方法 handleMainType2 main.data 的类型只有 Data 和 Data1/Data2 无法按类型区分。可区分的是运行时类:
def handleMainType2(main: Main): Unit =
println(main.data.getClass)
//class App$Data$Data1
//class App$Data$Data2
所以你可以定义
def handleMainType2(main: Main): Unit =
assert(main.data.getClass.isAssignableFrom(classOf[Data2]))
与运行时行为。
如果您想要编译时行为,那么您可以尝试将 handleMainType2 设为宏并在宏中使用运行时反射
// in a different subproject
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def handleMainType2(main: Main): Unit = macro handleMainType2Impl
def handleMainType2Impl(c: blackbox.Context)(main: c.Tree): c.Tree = {
import c.universe._
val clazz = c.eval(c.Expr[Main](c.untypecheck(main))).data.getClass
if (!clazz.isAssignableFrom(classOf[Data2]))
c.abort(c.enclosingPosition, s"${clazz.getName} <:!< Data2")
else q"()"
}
handleMainType2(Main(Data1(1))) // doesn't compile
handleMainType2(Main(Data2("a"))) // compiles
如果您不想将 handleMainType2 设为宏本身,您甚至可以将宏设为隐式。
trait IsData2[D <: Data with Singleton]
object IsData2 {
implicit def mkIsData2[D <: Data with Singleton]: IsData2[D] = macro mkIsData2Impl[D]
def mkIsData2Impl[D <: Data with Singleton : c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val clazz = c.eval(c.Expr[ValueOf[D]](c.untypecheck(
c.inferImplicitValue(weakTypeOf[ValueOf[D]], silent = false)
))).value.getClass
if(!clazz.isAssignableFrom(classOf[Data2])) {
c.abort(c.enclosingPosition, s"${weakTypeOf[D]} <:!< Data2")
} else q"new IsData2[${weakTypeOf[D]}] {}"
}
object App {
val m1: Main = Main(Data1(1))
val m2: Main = Main(Data2("a"))
}
def handleMainType2(main: Main)(implicit ev: IsData2[main.data.type]) = ()
handleMainType2(App.m1) // doesn't compile
handleMainType2(App.m2) // compiles
请注意handleMainType2(Main(Data2("a"))) 甚至
val m2: Main = Main(Data2("a"))
handleMainType2(m2)
不管用。