Scala课堂-6-高级类型

因为Kafka是用Scala写的,为了方便学习Scala语法,此处把Twitter官方的Scala课堂转载到此处。
(原文位于http://twitter.github.io/scala_school/zh_cn/index.html,由于时常被墙,速度极慢)。

视界(“View”)
有时候,你并不需要指定一个类型是等/子/超于另一个类,你可以通过转换这个类来伪装这种关联关系。一个视界指定一个类型可以被“看作是”另一个类型。这对对象的只读操作是很有用的。 隐 函数允许类型自动转换。更确切地说,在隐式函数可以帮助满足类型推断时,它们允许按需的函数应用。例如:

scala> implicit def strToInt(x: String) = x.toInt
strToInt: (x: String)Int

scala>  "123"
res0: String = 123

scala> val y:Int="123"
y: Int = 123

scala>  math.max("123", 111)
res1: Int = 123

视界,就像类型边界,要求对给定的类型存在这样一个函数。您可以使用<%指定类型限制,例如

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

这是说 A 必须“可被视”为 Int 。让我们试试

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

scala>  (new Container[String]).addIt("123")
res2: Int = 246

scala>  (new Container[Int]).addIt(123) 
res3: Int = 246

scala> (new Container[Float]).addIt(123.2F)
<console>:10: error: No implicit view available from Float => Int.
              (new Container[Float]).addIt(123.2F)

 其他类型限制
方法可以通过隐含参数执行更复杂的类型限制。例如,List 支持对数字内容执行 sum,但对其他内容却不行。可是 Scala 的数字类型并不都共享一个超类,所以我们不能使用 T

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
res5: Int = 246

scala>  (new Container("123")).addIt
<console>:10: error: Cannot prove that String =:= Int.
               (new Container("123")).addIt

使用视图进行泛型编程
在 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 })
res1: 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)
  }
}

上下文边界和 implicitly[]
Scala2.8引入了一种串联和访问隐式参数的快捷方式。

scala> def foo[A](implicit x: Ordered[A]) {}
foo: [A](implicit x: Ordered[A])Unit

scala>  def foo[A : Ordered] {}        
foo: [A](implicit evidence$1: Ordered[A])Unit

隐式值可能会通过 implicitly 被访问

scala>  implicitly[Ordering[Int]]
res5: Ordering[Int] = scala.math.Ordering$Int$@4b7773c4

更高级多态性类型 和 特设多态性
Scala 可以对“更高阶”的类型进行抽象。例如,假设您需要用几种类型的容器处理几种类型的数据。你可能定义了一个 Container 的接口,它可以被实现为几种类型的容器:Option、List 等。你要定义可以使用这些容器里的值的接口,但不想确定值的类型。 这类似与函数柯里化。例如,尽管“一元类型”有类似 List[A] 的构造函数,这意味着我们必须满足一个“级别”的类型变量来产生一个具体的类型(就像一个没有柯里化的函数需要只提供一个参数列表来被调用),更高阶的类型需要更多。

scala>  trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
defined trait Container

scala>  val container = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
container: Container[List] = $anon$1@434013d9

scala>  container.put("hey")
res1: List[String] = List(hey)

scala>  container.put(123)
res2: List[Int] = List(123)

注意: Container 是参数化类型的多态(“容器类型”)。 如果我们结合隐式转换 implicits 使用容器,我们会得到“特设的”多态性:即对容器写泛型函数的能力。

scala>  trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
defined trait Container

scala> implicit val listContainer = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
listContainer: Container[List] = $anon$1@54f3d86c

scala> implicit val optionContainer = new Container[Some] { def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get }
optionContainer: Container[Some] = $anon$1@591287f8

scala> :paste
// Entering paste mode (ctrl-D to finish)

def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = {
      val c = implicitly[Container[M]]                             
      c.put(c.get(fst), c.get(snd))
      }

// Exiting paste mode, now interpreting.

tupleize: [M[_], A, B](fst: M[A], snd: M[B])(implicit evidence$1: Container[M])M[(A, B)]

scala>  tupleize(Some(1), Some(2))
res0: Some[(Int, Int)] = Some((1,2))

scala>  tupleize(List(1), List(2))
res1: List[(Int, Int)] = List((1,2))

F-界多态性
通常有必要来访问一个(泛型)特质的具体子类。例如,想象你有一些泛型特质,但需要可以与它的某一子类进行比较。

trait Container extends Ordered[Container]

然而,现在比较方法是必须的了

def compare(that: Container): Int

因此,我们不能访问具体子类型,例如

class MyContainer extends Container {
  def compare(that: MyContainer): Int
}

编译失败,因为我们对 Container 指定了 Ordered 特质,而不是对特定子类型指定的。 为了调和这一点,我们改用 F-界的多态性。

trait Container[A <: Container[A]] extends Ordered[A]

奇怪的类型!但可以看到怎样对 A 实现了 Ordered 参数化,它本身就是 Container[A]

class MyContainer extends Container[MyContainer] { 
  def compare(that: MyContainer) = 0 
}

他们是有序的了:

scala>  List(new MyContainer, new MyContainer, new MyContainer)
res0: List[MyContainer] = List(MyContainer@50e205df, MyContainer@26ef9cf5, MyContainer@3d29accb)

scala>  List(new MyContainer, new MyContainer, new MyContainer).min
res1: MyContainer = MyContainer@622e8c17

鉴于他们都是 Container[] 的子类型,我们可以定义另一个子类并创建 Container[] 的一个混合列表:

scala> class YourContainer extends Container[YourContainer] { def compare(that: YourContainer) = 0 }
defined class YourContainer

scala> List(new MyContainer, new MyContainer, new MyContainer, new YourContainer)               
res2: List[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: Object]]] = List(MyContainer@58f59add, MyContainer@648a50cb, MyContainer@34be72fe, YourContainer@436f9cbf)

注意结果类型是怎样成为 YourContainer 和 MyContainer 类型确定的下界。这是类型推断的工作。有趣的是,这种类型甚至不需要是有意义的,它只是提供了一个合乎逻辑的最大下界为列表的统一类型。如果现在我们尝试使用 Ordered 会发生什么?

scala> (new MyContainer, new MyContainer, new MyContainer, new YourContainer).min
<console>:11: error: value min is not a member of (MyContainer, MyContainer, MyContainer, YourContainer)
              (new MyContainer, new MyContainer, new MyContainer, new YourContainer).min

对统一的类型 Ordered[]不存在了。太糟糕了。

 结构类型
Scala 支持 结构类型 structural types — 类型需求由接口 构造 表示,而不是由具体的类型表示。

scala>  def foo(x: { def get: Int }) = 123 + x.get
foo: (x: AnyRef{def get: Int})Int

scala>  foo(new { def get = 10 })                 
res4: Int = 133

 抽象类型成员
在特质中,你可以让类型成员保持抽象。

scala>  trait Foo { type A; val x: A; def getX: A = x }
defined trait Foo

scala> (new Foo { type A = Int; val x = 123 }).getX   
res5: Int = 123

scala>  (new Foo { type A = String; val x = "hey" }).getX
res6: String = hey

在做依赖注入等情况下,这往往是一个有用的技巧。

您可以使用 hash 操作符来引用一个抽象类型的变量:

scala> trait Foo[M[_]] { type t[A] = M[A] }
defined trait Foo

scala> val x: Foo[List]#t[Int] = List(1)
x: List[Int] = List(1)

 类型擦除和清单
正如我们所知道的,类型信息在编译的时候会因为 擦除 而丢失。 Scala 的 清单(Manifests) 功能,使我们能够选择性地恢复类型信息。清单提供了一个隐含值,根据需要由编译器生成。

scala>  class MakeFoo[A](implicit manifest: Manifest[A]) { def make: A = manifest.erasure.newInstance.asInstanceOf[A] }
defined class MakeFoo

scala> (new MakeFoo[String]).make
res7: String = ""
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值