javaweb icon indicating copy to clipboard operation
javaweb copied to clipboard

scala 学习(五)

Open www1350 opened this issue 7 years ago • 0 comments

类型和多态

类型

  • 参数化多态性 粗略地说,就是泛型编程
  • (局部)类型推断 粗略地说,就是为什么你不需要这样写代码val i: Int = 12: Int
  • 存在量化 粗略地说,为一些没有名称的类型进行定义
  • 视窗 我们将下周学习这些;粗略地说,就是将一种类型的值“强制转换”为另一种类型

变性 Variance

Scala的类型系统必须同时解释类层次和多态性。类层次结构可以表达子类关系。在混合OO和多态性时,一个核心问题是:如果T’是T一个子类,Container[T’]应该被看做是Container[T]的子类吗?变性(Variance)注解允许你表达类层次结构和多态类型之间的关系:

含义 Scala 标记
协变covariant C[T’]是 C[T] 的子类 [+T]
逆变contravariant C[T] 是 C[T’]的子类 [-T]
不变invariant C[T] 和 C[T’]无关 [T]

当我们定义一个协变类型List[A+]时,List[Child]可以是List[Parent]的子类型。

当我们定义一个逆变类型List[-A]时,List[Child]可以是List[Parent]的父类型。

Scala的协变

scala> class Student{}
defined class Student

scala> class BadStudent extends Student
defined class BadStudent

scala>  class Covariant[T](t:T)
defined class Covariant

scala> val cov = new Covariant[BadStudent](new BadStudent())
cov: Covariant[BadStudent] = Covariant@2aaf152b

scala> val cov2:Covariant[Student] = cov
<console>:14: error: type mismatch;
 found   : Covariant[BadStudent]
 required: Covariant[Student]
Note: BadStudent <: Student, but class Covariant is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
       val cov2:Covariant[Student] = cov
                                     ^

scala>  class Covariant[+T](t:T){}
defined class Covariant

scala> val cov = new Covariant[BadStudent](new BadStudent)
cov: Covariant[BadStudent] = Covariant@708eb310

scala> val cov2:Covariant[Student] = cov
cov2: Covariant[Student] = Covariant@708eb310

Scala的逆变

scala>   class Contravariant[-T](t: T)
defined class Contravariant

scala> val ct = new Contravariant[BadStudent](new BadStudent())
ct: Contravariant[BadStudent] = Contravariant@1a3e8e24

scala> val ct2:Contravariant[Student] = ct
<console>:14: error: type mismatch;
 found   : Contravariant[BadStudent]
 required: Contravariant[Student]
       val ct2:Contravariant[Student] = ct
                                        ^



scala> val ct3 = new Contravariant[Student](new Student())
ct3: Contravariant[Student] = Contravariant@77bc2e16

scala> val ct4:Contravariant[BadStudent] = ct3
ct4: Contravariant[BadStudent] = Contravariant@77bc2e16

边界

下界lower bounds

scala> def cacophony[T](things: Seq[T]) = things map (_.sound)
<console>:7: error: value sound is not a member of type parameter T
       def cacophony[T](things: Seq[T]) = things map (_.sound)
                                                        ^

scala> def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[java.lang.String]

scala> biophony(Seq(new Chicken, new Bird))
res5: Seq[java.lang.String] = List(cluck, call)

类型下界也是支持的,这让逆变和巧妙协变的引入得心应手。List[+T]是协变的;一个Bird的列表也是Animal的列表。List定义一个操作::(elem T)返回一个加入了elem的新的List。新的List和原来的列表具有相同的类型:

scala> val flock = List(new Bird, new Bird)
flock: List[Bird] = List(Bird@7e1ec70e, Bird@169ea8d2)

scala> new Chicken :: flock
res53: List[Bird] = List(Chicken@56fbda05, Bird@7e1ec70e, Bird@169ea8d2)

List 同样 定义了::[B >: T](x: B) 来返回一个List[B]。请注意B >: T,这指明了类型B为类型T的超类。这个方法让我们能够做正确地处理在一个List[Bird]前面加一个Animal的操作:

scala> new Animal :: flock
res59: List[Animal] = List(Animal@11f8d3a8, Bird@7e1ec70e, Bird@169ea8d2)

View Bound <%

Scala还有一种视图绑定的功能,如

    class Bird {def sing = {}}
    class Toy {}
    class Consumer[T <% Bird]() {
        def use(t: T) = t.sing
    }

或者类型参数在方法上:

    class Bird {def sing = {}}
    class Toy {}
    class Consumer() {
        def use[T <% Bird](t: T) = t.sing
    }
    class Test extends App {
        val c = new Consumer()
        c.use(new Toy)
    }

它要求T必须有一种隐式转换能转换成Bird,也就是 T => Bird,否则上面的代码会编译出错: No implicit view available from Toy => Bird. 加入一个隐式转换,编译通过。

    import scala.language.implicitConversions
    class Bird {def sing = {}}
    class Toy {}
    class Consumer() {
        def use[T <% Bird](t: T) = t.sing
    }
    class Test extends App {
        implicit def toy2Bird(t: Toy) = new Bird
        val c = new Consumer()
        c.use(new Toy)
    }

Context Bound

context bound在Scala 2.8.0中引入,也被称作type class pattern。 view bound使用A <% String方式,context bound则需要参数化的类型,如Ordered[A]。 它声明了一个类型A,隐式地有一个类型B[A],语法如下:

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] 更清晰的一个例子:

def f[A : ClassManifest](n: Int) = new Array[A](n) 又比如

`def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)`

其他类型限制

方法可以通过隐含参数执行更复杂的类型限制。例如,List支持对数字内容执行sum,但对其他内容却不行。可是Scala的数字类型并不都共享一个超类,所以我们不能使用T <: Number。相反,要使之能工作,Scala的math库对适当的类型T 定义了一个隐含的Numeric[T]。 然后在List定义中使用它: sum[B >: A](implicit num: Numeric[B]): B 如果你调用List(1,2).sum(),你并不需要传入一个 num 参数;它是隐式设置的。但如果你调用List("whoop").sum(),它会抱怨无法设置num。

在没有设定陌生的对象为Numeric的时候,方法可能会要求某种特定类型的“证据”。这时可以使用以下类型-关系运算符:

A =:= B A 必须和 B相等 A <:< B A 必须是 B的子类 A <%< B A 必须可以被看做是 B

scala> class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value }
defined class Container

scala> (new Container(123)).addIt
res11: Int = 246

scala> (new Container("123")).addIt
<console>:10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int]

类似地,根据之前的隐式转换,我们可以放松约束为可视性:

scala> class Container[A](value: A) { def addIt(implicit evidence: A <%< Int) = 123 + value }
defined class Container

scala> (new Container("123")).addIt
res15: Int = 246

使用视图进行泛型编程

在Scala标准库中,视图主要用于实现集合的通用函数。例如“min”函数(在 Seq[] 上)就使用了这种技术:

def min[B >: A](implicit cmp: Ordering[B]): A = {
  if (isEmpty)
    throw new UnsupportedOperationException("empty.min")

  reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
}

其主要优点是:

  • 集合中的元素并不是必须实现 Ordered 特质,但 Ordered 的使用仍然可以执行静态类型检查。
  • 无需任何额外的库支持,你也可以定义自己的排序:
scala> List(1,2,3,4).min
res0: Int = 1

scala> List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a })
res3: Int = 4

作为旁注,标准库中有视图来将 Ordered 转换为 Ordering (反之亦然)。

trait LowPriorityOrderingImplicits {
  implicit def ordered[A <: Ordered[A]]: Ordering[A] = new Ordering[A] {
    def compare(x: A, y: A) = x.compare(y)
  }
}

www1350 avatar May 22 '17 13:05 www1350