我已经知道:
<:
是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")
}
但我想知道何时需要使用<:
和<:<
?
如果我们已经有<:
,为什么我们需要<:<
?
谢谢!
答案 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))
此处推理器发现Int
和List[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
答案 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
另一方面,广义类型约束<:<
有两个用途:
您可以将其用作推断某些类型的事后证明。如:
class List[+A] {
def sum(implicit ev: A =:= Int) = ???
}
您可以创建任何类型的列表,但只有在sum
实际为A
的证据时才能调用Int
。
您可以使用上述“证据”来推断更多类型。这允许您分两步而不是一步推断类型。
例如,在上面的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
最终会以Nothing
或Any
或编译器所需的任何内容(通常为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>
所以,我认为这是主要区别。