<:<之间有什么不同和<:在scala中

时间:2013-11-07 06:57:39

标签: scala types implicit

我已经知道:

  • <:是Scala语法类型约束
  • <:<是利用Scala隐式到达类型constrait的类型

例如:

object Test {
  // the function foo and bar can have the same effect

  def foo[A](i:A)(implicit ev : A <:< java.io.Serializable) = i
  foo(1) // compile error
  foo("hi")

  def bar[A <: java.io.Serializable](i:A) = i
  bar(1) // compile error
  bar("hi")
}

但我想知道何时需要使用<:<:<

如果我们已经有<:,为什么我们需要<:<

谢谢!

4 个答案:

答案 0 :(得分:20)

两者之间的主要区别在于<:是对类型的约束,而<:<是编译器必须找到证据的类型,当用作隐式参数时。这对我们的程序意味着,在<:情况下,类型推理器将尝试找到满足此约束的类型。 E.g。

def foo[A, B <: A](a: A, b: B) = (a,b)

scala> foo(1, List(1,2,3))
res1: (Any, List[Int]) = (1,List(1, 2, 3))

此处推理器发现IntList[Int]具有公共超类型Any,因此它推断A满足B <: A

<:<限制性更强,因为类型推理器在隐式解析之前运行。因此,当编译器试图找到证据时,类型已经修复。 E.g。

def bar[A,B](a: A, b: B)(implicit ev: B <:< A) = (a,b)

scala> bar(1,1)
res2: (Int, Int) = (1,1)

scala> bar(1,List(1,2,3))
<console>:9: error: Cannot prove that List[Int] <:< Int.
              bar(1,List(1,2,3))
                 ^

答案 1 :(得分:7)

1.  def bar[A <: java.io.Serializable](i:A) = i

&lt;: - 保证类型参数 A i 实例将是可序列化

的子类型
2. def foo[A](i:A)(implicit ev : A <:< java.io.Serializable) = i

&LT;:其中 - 保证执行上下文将包含 A 类型的隐式值( ev 参数)可序列化的子类型。 这隐式在Predef.scala和 foo 方法中定义,并证明类型参数A的实例是可序列化的子类型:

implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

使用&lt;:&lt;的虚构情况操作者:

class Boo[A](x: A) {
  def get: A = x
  def div(implicit ev : A <:< Double) = x / 2
  def inc(implicit ev : A <:< Int) = x + 1
}

val a = new Boo("hi")
a.get  // - OK
a.div  // - compile time error String not subtype of Double
a.inc  // - compile tile error String not subtype of Int

val b = new Boo(10.0)
b.get  // - OK
b.div  // - OK
b.inc  // - compile time error Double not subtype of Int

val c = new Boo(10) 
c.get  // - OK
c.div  // - compile time error Int not subtype of Double
c.inc  // - OK
  • 如果我们不调用不符合的方法&lt;:&lt;条件比所有编译和执行。

答案 2 :(得分:3)

<:<:<之间肯定存在差异;这是我试图解释你应该选择哪一个。

我们来两节课:

trait U
class V extends U
  • 始终使用类型约束<:,因为它驱动类型推断。这是它唯一能做的事情:限制左侧的类型。

    必须在某处引用约束类型,通常在参数列表(或返回类型)中引用,如:

    def whatever[A <: U](p: A): List[A] = ???
    

    这样,如果输入不是U的子类,编译器将抛出错误,同时允许您按名称引用输入的类型以供以后使用(例如在返回时)类型)。请注意,如果您没有第二个要求,则所有这些都不是必需的(例外情况......),如:

    def whatever(p: U): String = ??? // this will obviously only accept T <: U
    
  • 另一方面,广义类型约束<:<有两个用途:

    1. 您可以将其用作推断某些类型的事后证明。如:

      class List[+A] {
        def sum(implicit ev: A =:= Int) = ???
      }
      

      您可以创建任何类型的列表,但只有在sum实际为A的证据时才能调用Int

    2. 您可以使用上述“证据”来推断更多类型。这允许您分两步而不是一步推断类型。

      例如,在上面的List课程中,您可以添加flatten方法:

      def flatten[B](implicit ev: A <:< List[B]): List[B]
      

      这不仅仅是一个证明,这是一种获取内部类型B 的方法,A现已修复

      这也可以在同一个方法中使用:想象你想编写一个实用程序sort函数,并且你想要元素类型T和集合类型Coll。您可能想写下以下内容:

      def sort[T, Coll <: Seq[T]](l: Coll): Coll
      

      T并不局限于其中的任何内容:它不会出现在参数或输出类型中。因此,T最终会以NothingAny或编译器所需的任何内容(通常为Nothing)结束。但是有了这个版本:

      def sort[T, Coll](l: Coll)(implicit ev: Coll <:< Seq[T]): Coll
      

      现在T出现在参数的类型中。将有两个推理运行(每个参数列表一个):Coll将被推断为给出的任何内容,然后,稍后将查找隐式,如果找到,T将是推断Coll现已修复。这实际上是从先前推断的T中提取类型参数Coll

基本上,<:<检查(并可能推断)类型作为隐式解析的副作用,因此它可以在不同的位置/不同于类型参数推断的时间使用。当他们碰巧做同样的事情时,坚持<:

答案 3 :(得分:1)

经过一番思考,我觉得它有些不同。 例如:

object TestAgain {
  class Test[A](a: A) {
    def foo[A <: AnyRef] = a
    def bar(implicit ev: A <:< AnyRef) = a
  }

   val test = new Test(1)
   test.foo // return 1
   test.bar // error: Cannot prove that Int <:< AnyRef.
}
这个menas:

  • <:的范围就在方法param generic tpye scope foo[A <: AnyRef]中。在示例中,方法foo具有通用tpye A,但不包括类A中的Test[A]
  • <:<的范围将首先找到方法的泛型类型,但方法bar没有param泛型类型,因此它将找到Test[A]的泛型类型。 / LI>

所以,我认为这是主要区别。