scala中的协变与逆变

Posted on 2010-01-12 17:20 xsong 阅读(701) 评论(0)  编辑  收藏 所属分类: scala
    java中的泛型:
List<String> s1=new ArrayList<String>();
        s1.add(
"hello");
当在s1中添加 String 类型以外的对象时,编译器会报错。
s1.add(new Date());
scala 中是用 [] 代替 java 中的<> 表达泛型。scala 在new ArrayList时,不需要指定String类型,得益与scala 的类型推断。
val strs : List[String]=new ArrayList()
scala 中定义泛型列表,和java中类似
class Link[T](val head: T, val tail: Link[T]) 
协变的定义:    假设有类G(或者接口和特征) 和类型T1,T2  。 在T1是T2的子类的情况下, 如果G<T1> 也是G2<T2>的子类,则类G是协变的。

当我定义一个列表对象li,并定义一个ln为li 的别名
 List<Integer> li=new ArrayList<Integer>();
        li.add(
1);
        List
<Number> ln=li;

这样的方式,编译器不能通过。
通过协变,可以实现上诉功能
class Link[+T](val head : T, val tail: Link[T]) {}

def main(args : Array[String])
{
      val ln : Link[Integer]
=new Link[Integer](1,null)
      val lnn : Link[Number]
=ln
      println(lnn.head)
}


但是如果试图加入一个方法,追加一个参数,并返回Link, 则会产生编译错误
class Link[+T](val head : T, val tail: Link[T]) {

 def prepend(newHead: T): Link[T] 
= new Link(newHead, this)
}

 
实际上,范型变为协变之后就不能把类型参数不加修改的放在成员方法的参数上(这里是newHead)了。但是,通过将成员方法定义为范型,并按照如下所示描述后就可以避免该问题了

class Link[+T](val head: T, val tail: Link[T]) {  
    def prepend[U 
>: T](newHead: U): Link[U] = new Link(newHead, this)  
}
 
scala 逆变,类似协变
假设有类 G 和类型T1,T2, 在T1 是T2的子类的情况,如果G[T2]是G[T1]的子类, 则G为逆变。
假设含有 apply的类 LessThan(apply 的逻辑 当a<b 时,返回true 否则返回false)
abstract class LessThan[T] {
  def apply(a: T, b: T): Boolean
}
val hashCodeLt: LessThan[Any] = new LessThan[Any] {
      def apply(a: Any, b: Any): Boolean 
= a.hashCode < b.hashCode
    }


    val strLt: LessThan[String] 
= hashCodeLt

    
assert(true ,strLt("a","b"))
在非变的情况 编译器会报错:Error:Error:line (18)error: type mismatch;
found   : xxx.LessThan[Any]
required: xxx.LessThan[String]
val strLt: LessThan[String] = hashCodeLt
在需要LessThan[Any]的地方使用了LessThan[String],由此看来LessThan不是逆变的,也不是协变,可以成为非变。
但是在类的定义前加一个 - 符号,使用逆变, 代码则可编译过去了。
abstract class LessThan[-T] {
  def apply(a: T, b: T): Boolean
}




只有注册用户登录后才能发表评论。


网站导航: