1. A类型等同于B类型
A =:= B
1.1 符号定义
sealed abstract class =:=[From, To] extends (From => To) with Serializable
1.2 符号说明
- =:=其实是中缀写法,可以看作From =:= To
- 父类From => To是Function[-From, +To]的另一种写法,相当于继承了trait Function1[-T1, +R],实例化需要实现Function1[-T, +R]中的apply方法
- 因为继承了Function[-From, +To],因此可以将 From =:= To看作是From => To的子类,遵循函数继承的规律:子类函数定义域大于父类,子类函数值域小于父类
2. A类型是B类型的严格子类型
A <:< B
2.1 符号定义
sealed abstract class <:<[From, To] extends (From => To) with Serializable
2.2 符号说明(略)
3. A可以当做B,即A为B的子类型或A可以隐式转换为B
A <%< B
4. 使用场景
4.1 <:<的使用
<:<符号必须使用柯里化函数在隐式参数中定义(带implicit前缀)
def test[T](i:T)(implicit ev: T <:< java.io.Serializable){println("ok")}
test("hi") // 正常
test(2) // 报错
- test方法中有一个隐式参数ev,类型为T <:< java.io.Serializable,表示参数类型T是java.io.Serializable的子类型
- 上下文中没有ev的定义,该隐式值由Predef提供
- private[this] final val singleton_<:< = new <:< [Any, Any]{def apply(x: Any):Any = x}
- implicit def conform[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
- 当调用test(“hi”)时,编译器推断出T是String,在寻找String <:< java.io.Serializable类型的隐式参数
- 上下文中找不到String <:< java.io.Serializable,于是通过conforms隐式方法产生一个
- conforms方法只有一个类型参数,他产生的结果是<:<[String, String]类型的对象
- 根据<:<抽象类的签名,<:<[-From, +To]第一个类型参数是逆变的,第二个参数是协变的
- 对<:<[-From, +To],子类的第一个参数要大于等于父类第一个参数的类型范围,子类的第二个参数要小于等于父类第二个参数的类型范围
- <:<[String,String]是<:<[String, java.io.Serializable]的子类
- 调用test(2)时,因为隐式方法产生的<:<[Int, Int]不符合<:<[Int, java.io.Serializable]子类型,抛出了异常
另外,对于Type类型,在判断之间的关系时也有类似的写法,不过这里是Type类型的方法:
typeOf[List[_]] =:= typeOf[List[AnyRef]] // false
typeOf[List[Int]] =:= typeOf[Iterable[Int]] // true
4.2 <:<与<:的区别
4.2.1 A <: B的执行过程
根据较小的类A寻找一个合适的类作为上界,这个合适的类是B或B的父类,如果是B则说明是A的直接或间接父类,如果是B的父类,则说明B被向上强制转型
- 根据A类型,向上查找A类型的超类是否是B类型,若是,则结束,若否,则进行第2步
- 查看超类的其他直接子类中是否有B类型,若是,则结束,若否,则返回第1步继续查找
- 若上界为AnyVal,则A和B都是数值类型
- 若上界为AnyRef,则A和B都是容器类型(或自定义类型)
- 若上界为Any,则A和B分别是数值类型和容器类型(或自定义类型)
// 举例
class A
class B extends A
class C extends A
def test[A, B <: A](a: A, b: B) = (a, b) // 我们返回(a, b)是为了更好的观察a和b的类型
test(new B, new C) // (A, C) = (B@3dd2a70d,C@7ecf001f),B被向上转型为父类A
4.2.2 A <:< B的执行过程
要求B必需是严格是A的父类或超类,不能是某个超类的子类,此时寻找B的过程相对简单,不断向上
- 根据A类型,向上查找A类型的超类是否是B类型,若是,则结束,若否,则递归进行第1步
- 若搜索达到定级类型仍未发现B类型,则报错
// 举例
class A
class B extends A
class C extends A
def test[A,B](a: A, b: B)(implicit ev: B <:< A) = (a, b)
test(new A, new C) // (A, C) = (A@29478556,C@2d0ea3a9),A为C的父类或超类
test(new B, new C) // 报错:error: Cannot prove that C <:< B,因为C向上搜索直至Any都没有找到B类型,因此报错