如何在编译时进行类型检查?

时间:2010-12-09 23:58:37

标签: scala types traits

TraversableOnce中,有一个sum方法只有在包含的类型为Numeric时才可用(否则它将无法编译)。我想知道这是否可用于其他情况(以避免运行时检查)。

特别是我们有两个特征A和B的情况。我们希望有一个方法f只有在对象继承 A和B时才能使用。但不是如果它只延伸其中一个。我不想再制作另一个trait AB extends A with B。我只是希望无法使用f,如果不是两个特征都被继承。

package com.example

trait Base
trait Foo extends Base {
  def g = println("foo bar " + toString)
}
trait Bar extends Base {
  /* If this is both Foo and Bar, I can do more */
  def f = {
    if (!this.isInstanceOf[Foo]) error("this is not an instance of Foo")
    this.asInstanceOf[Foo].g
  }
}
object Test {
  def main(args: Array[String]): Unit = {
    object ab extends Foo with Bar
    object ba extends Bar with Foo
    object b extends Bar
    ab.f
    ba.f
    // I don't want next line to compile:
    try { b.f } catch { case e: RuntimeException => println(e) }
  }
}

编辑:解决方案,感谢@Aaron Novstrup

trait Bar extends Base { self =>
  def f(implicit ev: self.type <:< Foo) = {
    //self.asInstanceOf[Foo].g // [1]
    ev(this).g // [2]
  }
}

现在在main中,b.f无法编译。尼斯

编辑2:改变行[1]至[2]反映@Aaron Novstrup的答案变化

编辑3:不使用self反映@Aaron Novstrup回答的变化

trait Bar extends Base {
  /* If this is both Foo and Bar, I can do more */
  def f(implicit ev: this.type <:< Foo) = {
    ev(this).g
  }
}

2 个答案:

答案 0 :(得分:8)

是的,你可以:

trait A {
   def bar = println("I'm an A!")
}

trait B { 
   def foo(implicit ev: this.type <:< A) = { 
      ev(this).bar
      println("and a B!")
   }
}

如果对象的静态类型(在呼叫站点)扩展evidence,编译器将只能提供A参数。

答案 1 :(得分:3)

知道总和的签名是

def sum [B >: A] (implicit num: Numeric[B]) : B

您似乎假设数字类型扩展Numeric,但事实并非如此。实际上它们被隐式转换为 Numeric ,在 Int 的情况下,隐式使用的是 scala.math.Numeric.IntIsIntegral ,它们定义了像< em> plus 和

因此,允许通过隐式提供所需操作来实现对允许哪些类型 A a TraversableOnce [A] .sum 的限制。

这只是对数字和类型类的整体工作原理的快速解释。有关更多信息,请查看math.Numeric.XisY,math.Integral和math.Fractional以及类型类的工作原理:implicit-tricks-type-class-patterntype-class-pattern-example