このルールさえ押さえておけば、読んでいるコードが省略記法を使っていてもほぼ読めるようになります。
def concatAsString(a: Int, b: Int): String = {
val a_ = a.toString();
val b_ = b.toString();
return a_.+(b_);
}
セミコロンは省略できます。
def concatAsString(a: Int, b: Int): String = {
val a_ = a.toString()
val b_ = b.toString()
return a_.+(b_)
}
引数を持たない且つ、定義時に () ありで定義したメソッドは、呼び出し時に () を省略できます。
def concatAsString(a: Int, b: Int): String = {
val a_ = a.toString
val b_ = b.toString
return a_.+(b_)
}
なぜその様な特殊なルールになっているか、というと、理想としては
- 副作用を伴わないメソッド(getterみたいなやつ)は括弧を付けない
- 副作用を伴うメソッドは括弧をつける
という方向にしたいからです。
単純に定義時に () つけたメソッドは省略不可、定義時に () つけなかったメソッドは () の記述不可 としてしまうと、Java のライブラリを呼び出すときに整合性がつかなくなるので、 Javaとの互換を意識して一見すると妙なルールになっています。
そして、ブロックの最後の式の結果が、ブロックの結果となるので、return が省略できます。
def concatAsString(a: Int, b: Int): String = {
val a_ = a.toString
val b_ = b.toString
a_.+(b_)
}
単一の引数をとるメソッドは、ドットと引数グループの括弧を省略できます。
def concatAsString(a: Int, b: Int): String = {
val a_ = a.toString
val b_ = b.toString
a_ + b_
}
ドットを省略した場合、ドットを使った呼び出しよりも結合が弱くなります。
def concatAsString(a: Int, b: Int): String = {
a.toString + b.toString
}
ブロックが1つの式しか持たない場合、ブロックにする必要がなくなります。
def concatAsString(a: Int, b: Int): String = a.toString + b.toString
再帰したメソッドなどを除いて、戻り値の型が自明の場合は、戻り値の型アノテーションを省略できます。
def concatAsString(a: Int, b: Int) = a.toString + b.toString
できますが、public なメソッドに関しては、省略をおすすめしません。
val f: Function1[Int, String] = new Function1[Int, String] {
def apply(arg: Int): String = arg.toString
}
f.apply(10) // "10" が得られる
Function0
~ Function22
の型は =>
を使って記述する事ができます。
val f: (Int) => String = new Function1[Int, String] {
def apply(arg: Int): String = arg.toString
}
f.apply(10) // "10" が得られる
Function0
~ Function22
のインスタンスは、 =>
を使って記述することができます。
val f: (Int) => String = (arg: Int) => arg.toString
f.apply(10) // "10" が得られる
引数の型が自明の時は、型アノテーションを省略する事ができます。
val f: (Int) => String = (arg) => arg.toString
f.apply(10) // "10" が得られる
引数が一つの場合(つまりFunction1
の時)、引数グループの括弧を省略できます。
val f: Int => String = arg => arg.toString
f.apply(10) // "10" が得られる
全ての引数が、1回のみ使われる場合は、引数の宣言を省略し、_ で表現することができます。
val f: Int => String = _.toString
f.apply(10) // "10" が得られる
apply
という名前のメソッドは省略することができます。
val f: Int => String = _.toString
f(10) // "10" が得られる
match 式と同じ書き方で、Function1
もしくは PartialFunction
を定義することができます。
val pf: PartialFunction[Int, String] = {
case 1 => "AAA"
case 2 => "BBB"
}
pf(1) // "AAA"
pf(3) // MatchError が投げられる
PartialFunction
は Function1
のサブトレイトで、とりうる引数の値のうち、一部の値のみ結果を返す関数を表します。
Seq(1, 2, 3) map {
case 1 => "AAA"
case 2 => "BBB"
case _ => "ZZZ"
}
Function1
などを要求するメソッドの引数で、直接パターンマッチが使えるので非常に便利です。
メソッドから FunctionN
のインスタンスを生成できます。
def add(a: Int, b: Int): Int = a + b
というメソッドがあった時に、後ろに _
を付けると Function2
のインスタンスがとれます
val f = add _ // f: (Int, Int) => Int = <function2> と評価される
明確に FunctionN
が要求されている事が判明している場合は、 _
を省略できます。
val f: (Int, Int) => Int = add
大抵の場合は FunctionN
を引数にとるメソッドに渡す感じになるので _
はあまり使わないです。
じゃあなんでそんなルールになっているかというと、
println(add) // 引数を与え忘れ
とかした時に (Int, Int) => Int = <function2>
とか出力されても何も嬉しくないので、
明示的に FunctionN
を要求していない時は変換せずにコンパイルエラーにしてくれるように
そういったルールになっています。