scaladocを読むために

scaladoc読んで勉強してても記号が多いのとよく分からないキーワードが出てきて読みづらい。
気になったものを調べてまとめる。

よく出てくるのは以下だ。

  • AnyはJavaでいうObject型。
  • UnitはJavaでいうvoid。

可変長引数

以下はMap#+メソッド。*の部分。

def +(kvs: (A, B)*): Map[A, B]

implicitパラメータ

例えばList#maxByメソッド。

def maxBy[B](f: (A) ⇒ B)(implicit cmp: Ordering[B]): A

implicitが付いているメソッド引数は省略可能。省略すればデフォルトのものが適用されるが、自分で指定することも可能。
まず省略バージョン。

    val list = List("one", "two", "three", "four")
    val result = list.maxBy(_.length) // 一番文字列長の長いthreeが返る

次に並び替えの仕組みを独自Orderingで変える。無名traitを生成して対応。

    val result2 = list.maxBy(_.length){ // 一番文字列長の長いthreeが返る
      new Ordering[Int]{
        def compare(x: Int, y: Int) = x - y
      }
    }

    val result3 = list.maxBy(_.length){ // 一番文字列長の短いoneが返る
      new Ordering[Int]{
        def compare(x: Int, y: Int) = y - x
      }
    }

implicit付変数宣言をしておくと、第二引数省略してもそれが使用される。

implicit val ordering = new Ordering[Int]{
    def compare(x: Int, y: Int) = y - x
}
val result = list.maxBy(_.length) // one

カリー化されたメソッド

以下はList#foldのscaladoc。

def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1

このように使う。

val list = List(1,2,3,4)
val result = list.fold(10)((e1,e2) => e1 + e2) // 実際の処理は10+1+2+3+4が行われる

ただ、2番目の引数は(ではなく{で記述されることが多い。

val result = list.fold(10){(e1,e2) => e1 + e2}

ジェネリクス

Ancestor > Parent > Childというようなクラス階層になっているとして。

上限境界

以下の例だと、Parent及びサブクラスChild。

class Sample[T <: Parent](x: T) {}
下限境界

以下の例だと、Parent及びスーパークラスAncestor。

class Sample[T >: Parent(x: T) {}

先のfoldメソッドのscaladocをもう一度引用すると

def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1

となっている。このメソッドで使用するのはA1というAの下限境界型。よって、、、

class Ancestor(var x:Int) {}
class Parent(var y:Int) extends Ancestor(y){}
class Child(var z:Int) extends Parent(z) {}

val list:List[Parent] = List(new Parent(1), new Parent(2))
val result = list.fold(new Ancestor(10)){(e1, e2) => e2.x = e1.x + e2.x; e2;}

List[Parent]に対して、変数zには親のAncestorが渡せる。この場合、A1はAncestorになるので、e1.xというアクセスになる。
一方、変数zのところを、new Parent(10)としたならば、e1.xではコンパイルエラーとなり、正しくはe1.yとなる。

共変

Mapの場合、こんなふうになっている。

trait Map[A, +B] extends Iterable[(A, B)] with GenMap[A, B] with MapLike[A, B, Map[A, B]]

このvalueの型が+となっており、共変を表す。つまり以下はOKとなる。

var map1:Map[String, Parent] = Map("k1" -> new Parent())
var map2:Map[String, Child] = Map("k2" -> new Child())

map1 = map2

ただし、keyの型は共変ではないのでこれはダメ。コンパイルエラーとなる。

var map3:Map[Parent, String] = Map(new Parent() -> "v3")
var map4:Map[Child, String] = Map(new Child() -> "v4")

map3 = map4
反変

もし上記Mapのvalueの型に-がついて反変だったら、Map[String, Ancestor]のインスタンスがmap1に代入できる。