minimize

事業拡大のため、新しい仲間を募集しています。
→詳しくはこちら

Java と異なる点を中心にいくつか。

セミコロン(;)

行末のセミコロンは必要無い。
複数の文を1行に書きたいときだけ、セミコロンを間に記述する。

val a = 5
val b = 5; println("debug")

ダブルクォーテーション

Scala は Java と同じく、文字列リテラルにはダブルクォーテーションを使う。
シングルクォーテーションは文字(Char)リテラル扱いである。

return

Scala では、return キーワードは省略できる。
return を使わなくても全ての文は値を返す。

def func(b: Boolean) = {
  if (b) "TRUE" else "FALSE"
}

return を使う必要があるのは関数の途中で抜けたいときくらいだが
これは Bad Coding Style なので、なるべく使わないようにしよう。

プリミティブ型

Scala ではプリミティブ型という概念は無いのだが、Java のプリミティブ型を使うときには
以下のようにする。

val cnt: Int = 5

このように、先頭が大文字の型名を使用する。これは scala.Int クラスを表している。
ちなみにこれらはJavaのclassファイルにコンパイルされるときには integer 型(プリミティブ型)に変換される。
よってパフォーマンスの心配は不要だ。

Unit型

Java では戻り値が無いときに void を使ったが、Scala では代わりに Unit を使う。
これは「値が無い」ことだけを表現する型。() と記述する。

def func(): Unit = {
  ...
  () // Unit を返す。この記述は省略可能
}

override

Scala では、関数をオーバーライド(上書き)するときは明示的にキーワードを付けないとコンパイルエラーになる。

override def toString = { ... }

type

型のエイリアス(別名)機能。

type T = Int

このようにすると、コード中で型定義に T としている箇所は全て Int で置き換えられる。

複数定義

一文で複数の変数定義が可能。

val a, b, c = 5

ちなみにこれは a,b,c それぞれに 5 が代入される。

様々な括弧

Java では Generics を表現するのに <> を使ったが、Scala では [] を使う。
また、Java では配列にアクセスするのに [] を使ったが、Scala では () を使う。
配列は言語仕様ではなくリストなどと同じライブラリの一種となった。

Scala では、<> はXMLリテラルとして解釈される。
すなわち、こんな記述ができる。

case <id>{Text(Name)}</id> => true

素晴らしい。Scala は、XMLをサポートするために <> を Generics から解放したのだ。

定義キーワード

val は変数(変更不可能)、def は関数(変更不可能)。var は変更(=再代入)可能な変数または関数。

val weiget : Int = 5
def time : Long = { return System.currentTimeMillis }
var weiget : Int = 2
var time : () => Long = () => System.currentTimeMillis

ただし Java の final と同じように、例えば val で配列を宣言してもその中身は変更可能だ。

ワイルドカード

以下のような記述が可能。

import java.text.DateFormat._
import java.util.{Date, Locale}

アンダースコア(_)は、Java の * と同じ意味である。{ } はまぁ見ての通りだ。

ではなぜ * ではないのか。それは、Scala では * は識別子として有効な文字だからだ。
数値の掛け算などに使われる * は、演算子であり関数である(詳しくは後述)。

class Reference[T] {
  private var contents: T = _
}

こんなワイルドカードもある。
この場合、contents 変数は T 型のデフォルト値に初期化される。
数値型ならば 0、Boolean 型ならば false、Unit 型ならば ()、その他の型なら null という具合に。

変数の初期化

Scala では、変数は必ず初期化しなければならない。

class Sample {
  val num: Int
  var cnt: Int
}

val は変更不可能な変数なので当然初期化が必要だが、var においても同じだ。
ちなみに上のコードの場合、変数定義は未完と見なされこのクラスはコンパイルエラーとなる。

初期化するのに適切な値が無い場合(もしくは何でも良い場合)は、前述したワイルドカードを使う。

var cnt: Int = _

同値(==)演算子

Scala では、== による比較が equals 関数を使った同値比較になる。
これは case class を使うときに便利である。

繰り返しパラメータ

Java5 と同じように、Scala でも繰り返しパラメータが使える。

def friendsTimeline(options : OptionalParam*)

引数定義の型に * を付けるだけ。使い方は Java5 と同じなので省略。

演算子

Java では、演算子は言語上特別な扱いだったが Scala では、演算子は「関数の一種」である。
全てのものはオブジェクトであり、そのオブジェクトが(計算可能ならば)演算子関数を持っている。
例えば、以下のような文を考える。

1 + 2

Scala では、これすらも関数呼び出しとなる。

(1).+(2)

上の二つのコード、内部処理は全く同じだ。
1 というオブジェクト(Scalaでは数値もオブジェクト)の + という関数を、2 という引数を付けて呼び出している。

つまり、以下の文はどうだろう。

++a

これはコンパイルエラーになる。++ はどのオブジェクトの演算子関数なのか?Scalaには判らないからだ。

a += 1

と書こう。

省略構文

先の例で、1 + 2 は省略構文だ。
数値演算だけでなく、全ての場合においてこの省略構文が利用できる。

df.format(now)

df がオブジェクト(レシーバ)、format が関数、now は引数だ。
上の文は、以下のように記述できる。

df format now

文脈に応じて読みやすい方で記述すれば良い。

ちなみに、グローバルで定義した関数を

func 2

のようには呼び出せない。

func(2)

と書く必要がある。

ヒアドキュメント

Java ではできなかった複数行の文字列もそのまま記述できる。
ダブルクォーテーションを3連続で。

val hello = """world    
                              wide"""

また、この記述法だと " のエスケープ処理がいらなくなる。

val s = """It's a "true"."""

正規表現を書くときに役立つだろう。

ループ構文

Scala では、以下のようなループ構文が使用できる。

for (v <- args) println(v)

Java でいうところの以下と同じ。

for (v : args) println(v)

Scala では : という演算子は特別な目的で使われている。
また、見た目としても v <- args の方が直感的にわかりやすい(はず)。

ちなみに、上記のコードを Scala 流に書けば以下のようになる。

args.foreach(v => println(v))

フィルタ、yield

Scala では、特殊なループ構文も用意されている。

def sqrts(xs: List[double]): List[double] =
  for (x <- xs; 0 <= x) yield Math.sqrt(x)

上のコードは、xs リストの各要素の平方根を集めたリストを返す。
0 <= x の部分はフィルタと呼ばれ、この条件を満たす場合のみ yield 節の処理がされる。

ちなみに上の文は以下と同じとなる。

xs.filter(0 <= x).map(x => Math.sqrt(x))

<:, >:, <%

Scala には一見不可思議な記号がいくつも登場する。説明しよう。

Upper Bounds

T <: Service

こちらの意味は、Service のサブクラスである T という意味。
Java でいうところの T extends Service かな。

T <: Ordered[T]

こちらはどうだろう。これの意味は、Ordered[T] の振る舞いを持つ T という意味。
例えば T には RichInt などが適用できる。

こういった関係は conformance relation と呼ばれる。conformance は「順応」のような意味。
Service, Ordered[T] に順応できる T という感じか。

Lower Bounds

[T >: S]

これは、T が S の親クラス(スーパークラス)であるという意味。
逆に言えば、S が T のサブクラスであるという意味。
例えば [T >: ArrayList] と書けば、T には List や Collection などが適用できる。
Upper Bounds の逆だ。

View Bounds

A <% Ordered[A]

これは、Ordered[A] に変換可能な A という意味。詳しくは View Bounds 参照。

Covariant

Stack[+A]

これは、以下のような関係が成り立つことを表す。

val stack1 = Stack[List]
val stack2: Stack[Collection] = stack1

つまり、List は Collection のサブクラスであるから
Stack[List] もまた Stack[Collection] のサブクラスであるということ。
サブクラスだから、代入可能となる。
もし Stack の定義が Stack[A] だったら、上の2行目はコンパイルエラーになる。

Contravariant

Stack[-A]

これは Covariant の逆で、以下の関係が成り立つ。

val stack1 = Stack[List]
val stack2: Stack[Collection] = stack1

つまり、Stack[Collection] が Stack Stack[List] のサブクラスという扱いになる。
なんか変な気もするが、そういうことなのだ。
Contravariant を使う機会は滅多に無いかもしれない。