首页 > 解决方案 > Scala:在编译时验证类参数不是 instanceOf 特征

问题描述

在编译时,我想验证类参数不是特定特征 T 的实例。我知道如何在运行时使用require或 acase match但想知道如何在编译时完成以防止用户提供某种类型的对象混入。

我已经研究了 scala 宏/反射,但无法完全理解这一点。

trait A
trait B
trait T
abstract class C extends A with B

case class P(c: C){
  require(!c.isInstanceOf[T]) // how to do this at compile time ?
}

// usage as below
object c1 extends C
object c2 extends C
object c3 extends C
object c4 extends C with T

val l = List(c1, c2, c3, c4).map(k => P(k)) // should fail at compile time

标签: scalashapelessscala-macrosscala-reflect

解决方案


你不能用List. 中的所有元素都List(c1, c2, c3, c4)将具有相同的类型,即C其中一个具有类型的信息C with T将丢失。

new C {},new C with T {}是 的运行时值c1, c2, c3, c4,编译器在编译时无权访问它们List(c1, c2, c3, c4)

您可以使用HList. 使用shapeless.<:!<,shapeless.ops.hlist.LiftAll和 kind-projector

def noElementIsSubtypeOfT[L <: HList](l: L)(implicit liftAll: LiftAll[* <:!< T, L]) = null

noElementIsSubtypeOfT(c1 :: c2 :: c3 :: HNil) // compiles
// noElementIsSubtypeOfT(c1 :: c2 :: c3 :: c4 :: HNil) // doesn't compile

或者

def noElementIsSubtypeOfT[L <: HList : LiftAll[* <:!< T, *]](l: L) = null

对于类参数,您可以执行

case class P[U <: C](c: U)(implicit ev: U <:!< T)

P(c1) // compiles
P(c2) // compiles
P(c3) // compiles
// P(c4) // doesn't compile

或者

case class P[U <: C : * <:!< T](c: U)

推荐阅读