对于类型限制 =:=
和<:<
A =:= B //表示A类型等同于B类型
A <:< B //表示A类型是B类型的子类型
这个看上去很像操作符的=:=
和 <:<
,实际是一个类,它在Predef
里定义:
sealed abstract class =:=[From, To] extends (From => To) with Serializable
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
它定义了两个类型参数,所以可以使用中缀写法:From <:< To
2.10之前还有一个 <%<
类似于view bound,表示 A可以当作B,即A隐式转换成B也满足。但在2.10里已经废弃这种写法。
类型限制用在特定方法(specialized methods)的场景,所谓特定,是指方法只针对特定的类型参数才可以运行:
scala> def test[T](i:T)(implicit ev: T <:< java.io.Serializable) { print("OK") }
test: [T](i: T)(implicit ev: <:<[T,java.io.Serializable])Unit
scala> test("hi")
OK
scala> test(2)
<console>:9: error: Cannot prove that Int <:< java.io.Serializable.
上面定义的test方法,在方法的第二个参数使用了一个隐式参数ev,它的类型是:T <:< java.io.Serializable
,表示只有参数类型T
是java.io.Serializable
的子类型,才符合类型要求。
或许你会奇怪上面test方法调用”hi”时,隐式参数ev是从哪儿传入的?当前并没有定义这个隐式参数。这个隐式参数也是由Predef
里的隐式方法产生的
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
当调用test("hi")
,编译器推断出T是String
,在寻找 String <:< java.io.Serializable
类型的隐式参数时,上下文中找不到,于是通过conforms
隐式方法来产生一个,conforms
方法只有一个类型参数,它产生的结果是<:<[String,String]
类型的对象,但因为<:<[-From,+To]
第一个类型参数是逆变的,第二个类型参数是协变的,所以<:<[String,String]
符合<:<[String,java.io.Serializable]
的子类,满足要求。
而调用test(2)
时,因为隐式方法产生的<:<[Int,Int]
不符合<:<[Int,java.io.Serializable]
子类型,抛出了异常。可见这块编译器是利用函数类型的多态机制来实现类型检测的。
另外,对于Type类型,在判断之间的关系时也有类似的写法,不过这里是Type类型的方法:
scala> typeOf[List[_]] =:= typeOf[List[AnyRef]]
res4: Boolean = false
scala> typeOf[List[Int]] <:< typeOf[Iterable[Int]]
res1: Boolean = true
上面的是方法调用:typ1.=:=(typ2)
,虽然效果都是证明类型关系,但不要混淆。
//2013.11.8 补充
昨天泽彬在csug的旺旺群(94329267/csugcsug)里问<:
与<:<
的差异:
object A{
def test[T <: java.io.Serializable](i:T) {}
test(1) // 编译时报错
def test2[T](i:T)(implicit ev: T <:< java.io.Serializable) {}
test2(1) // 同样编译时报错
}
两者的效果似乎一样,应该怎么选择?他在stackoverflow上也问了一下:http://stackoverflow.com/questions/19829770/whats-different-between-and-in-scala
有人给出这样的解释:
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]
,显然这不符合 B <: A
的约束,编译器在做类型推导的时候,为了满足这个约束,会继续向上寻找父类型来匹配是否满足,于是在第一个参数被推导为Any
类型的情况下,List[Int]
符合Any
的子类型。
def bar[A,B](a: A, b: B)(implicit ev: B <:< A) = (a,b)
scala> bar(1,List(1,2,3))
<console>:9: error: Cannot prove that List[Int] <:< Int.
通过隐式参数ev来证明类型时,类型推断过程不会像上面那样再向上寻找可能满足的情况,而直接报错。
确实,在用 <:
声明类型约束的时候,不如用<:<
更严格,除了上面的类型推导,存在隐式转换的情况下:
scala> def foo[B, A<:B] (a:A,b:B) = print("OK")
scala> class A; class B;
scala> implicit def a2b(a:A) = new B
scala> foo(new A, new B) //存在A到B的隐式转换,也可满足
OK
scala> def bar[A,B](a:A,b:B)(implicit ev: A<:<B) = print("OK")
scala> bar(new A, new B) //隐式转换并不管用
<console>:17: error: Cannot prove that A <:< B.
Pingback: scala类型系统:柯里-霍华德同构 | 在路上
scala> typeOf[List[Int]] <: import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> typeOf[List[Int]] <: typeOf[List[Int]]
res24: reflect.runtime.universe.Type = scala.List[Int]
scala> typeOf[Iterable[Int]]
res25: reflect.runtime.universe.Type = scala.collection.mutable.Iterable[Int]
typeOf[A]是在哪里定义的, 找不到啊。 typeOf的语意没搞明白。
typeOf[List[_]] =:= typeOf[List[AnyRef]] 这里的意思也没搞明白
typeOf[A]是返回一个值??? 显然不是。
因为=:=[X,Y]要求 X, Y必须是类型。
这么说typeOf[A]是个类型构造器?typeOf[List[_]]返回的是类型。
可是typeOf[T]的类型构造器, 与List[T]的类型构造有什么区别呢?, 看下面的例子。
scala> typeOf[Int]
res34: reflect.runtime.universe.Type = Int
scala> List[Int]
:16: error: missing arguments for method apply in object List;
follow this method with `_’ if you want to treat it as a partially applied function
List[Int]
import scala.reflect.runtime.universe.typeOf;
这里 universe 是 runtime 这个 package object 里的一个值:
lazy val universe: api.JavaUniverse = new runtime.JavaUniverse
而 JavaUniverse继承自 Universe,去看Universe类就清楚了,它混入了一堆trait,包含TypeTags,
typeOf 方法就是 TypeTags 里面定义的:
/**
* Shortcut for `implicitly[TypeTag[T]].tpe`
* @group TypeTags
*/
def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe
这里还是没理解 typeOf[List[_]] =:= typeOf[List[AnyRef]]
这里作为中缀类型, 是否等价与 =:=[typeOf[List[_]], typeOf[List[AnyRef]]]
这么理解肯定是错误对的, 从你提供的情况来看, typeOf是个泛型函数, 返回类型为Type
scala> val a = typeOf[String];
a: reflect.runtime.universe.Type = String
scala> val b = typeOf[String];
b: reflect.runtime.universe.Type = String
scala> a =:= b
res3: Boolean = true
看来存在一个 =:= 的中缀函数。
不过我在Predef里没找到=:=这个函数。
能解释下我这里的 a =:= b的语意么?
Predef中=:=相关的内容如下
@implicitNotFound(msg = “Cannot prove that ${From} =:= ${To}.”)
sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }
object =:= {
implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}
我的理解是:
1. 有个=:=的泛型类
2. 有个类型为=:=[Any, Any]{ Refinement }的final对象 singleton_=:=
3. 有个半生类对象=:=, 里面有个隐式函数tpEquals[A], 返回类型为 =:=[A, A]
实际返回值为: singleton_=:=.asInstanceOf[A =:= A]
—————————————————————————–
—————————————————————————–
a. 由于 =:=是seal的类, 所以不能继承
b. 由于 =:=是abstract, 所以不能构造=:=实例
c. =:=只存在唯一的实例singleton_=:=
试图创建=:=的其他实例的错误如下:
scala> new =:=[String, String]{ def apply( x: String) : String = x }
:13: error: illegal inheritance from sealed class =:=
new =:=[String, String]{ def apply( x: String) : String = x }
^
这里伴生对象=:=的tpEquals干什么用的??? 能分析下么?
隐式转换,产生一个 =:=[A, A] 的类型
不明白为什么”This uses the Scala feature that a generic type op[T1, T2] can be written T1 op T2″?
就是A <:< A . 能不能解释一下?谢谢
搜一下 “中缀”表示符
老师您好,想向您请教,在
sealed abstract class =:=[From, To] extends (From => To) with Serializable
sealed abstract class <: To) with Serializable
中,=:=和<:<为什么需要继承Function2和Serializable?按照我的理解,=:=和<:<的实例主要用来作为evidence以达到类型限制的作用,那让其实现函数接口有什么意义呢?
谢谢老师!
继承Function2是为了继承apply(f:From): To方法,类型限制就是利用了这个方法
继承的难道不是Function1吗
最后一条那个隐式转换其实没有起作用,写不写其实都能执行成功,原因还是因为B被提升为Object了!
scala> def foo[B, A<:B] (a:A,b:B) = (a, b)
foo: [B, A class A; class B
defined class A
defined class B
scala> foo(new A, new B)
res2: (A, Object) = (A@6a552721,B@3815a7d1)