考慮以下實作草圖:
sealed trait Type
object Type {
case object Type1 extends Type
case object Type2 extends Type
}
sealed trait Data {
type T <: Type
}
object Data {
type Aux[TT] = Data {
type T = TT
}
case class Data1(i: Int) extends Data {
type T = Type1.type
}
case class Data2(s: String) extends Data {
type T = Type2.type
}
}
case class Main(
//lots of other fields
data: Data.Aux[T] forSome { type T <: Type}
)
// This method is supposed to accept the only Main's
// that have data of type Data.Aux[Type2.type]
def handleMainType2(
main: Main
): Unit = ???
問題:
case class包含存在型別的欄位是有可能實作一個方法,該方法將接受存在型別的唯一分支。
也許無形在這里會有所幫助?
uj5u.com熱心網友回復:
首先,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]))
q"()"
else c.abort(c.enclosingPosition, s"${clazz.getName} <:!< Data2")
}
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]))
q"new IsData2[${weakTypeOf[D]}] {}"
else c.abort(c.enclosingPosition, s"${weakTypeOf[D]} <:!< Data2")
}
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)
不管用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/511345.html
