Scala入門

クラス編

Scalaをやる時はね…

Scalaをやる時はね…画像

ってことで始めます

あじぇんだ

  1. クラスの作り方
  2. クラスとオブジェクト
  3. コンパニオンオブジェクト
  4. ケースクラス

最初にざっくりと文法を紹介して、その後ハンズオンでやります

githubにslideをアップロードしてあるので、コピペで使って下さい

SlideのURL

https://github.com/mironal/slide/blob/master/hyper-miuchi/2013-04-17/scala/sectiona/sectiona.md

twitterで流します

1. クラスの作り方

定義

class Greeter {
  // クラスの中味
}
// Greeter : 挨拶する人

インスタンス作成

val yamada = new Greeter();

val ueda = new Greeter();

コンストラクタ

クラスの初期化を行いたいときに使います

class Greeter {
  // ここに書いた処理がそのままコンストラクタの処理になる
  println("**********");
  println("Created !");
  println("**********");
}

引数のあるコンストラクタ

class Greeter(name: String) {
  def myName() = name
}
yamada = new Greeter("yamada")
yamada.myName()
// yamada

メンバーの定義

メソッド(関数のこと)

class Greeter(name: String) {
  // 公開メソッド
  def sayHello(name: String): Unit = {
    println( mkHello(name) )
    println( "My name is " + myName() )
  }

  // 非公開メソッド
  private def mkHello(name: String): String = {
    s"Hello! ${name}"
  }
}

メンバーの定義

フィールド(変数, 定数の事)

class Greeter(name: String){
  val myName = name
}

冗長なので以下のように書ける

class Greeter(val name: String)

オブジェクト(Javaのstatic的な奴)

シングルトンなものを表します.

例えばアプリケーションそのものは一つしか無いものです。

object SayHello {
  def say() = println("Hello")
}

SayHello.say()
// Hello

ざっくり終了

次からはハンズオンな感じで

例題

これ以降は複素数を表すComplexクラスを用いて説明をします.

エディタを使って以下のコードをComplex.scalaという名前で保存して下さい(コピペ可)

// real      : 実部
// imaginary : 虚部
class Complex(real: Double, imaginary: Double) {
  def r() = real
  def i() = imaginary
}

※sample/Complex1.scalaがソレです

続いて以下のコードを同じComplex.scalaの下に追加.

object ComplexNumbers {
  def main(args: Array[String]) {
    val c = new Complex(1.2, 3.4)
    println("real part     : " + c.r())
    println("imaginary part: " + c.i())
  }
}

書けたらComplex.scalaの在るディレクトリで以下のコマンドを実行.

scalac Complex.scala
scala ComplexNumbers
// real part     : 1.2
// imaginary part: 3.4

実行できましたか?

このコードを改善していきます

改善1

先ほどのコードのこの部分

println("real part     : " + c.r())
println("imaginary part: " + c.i())
この部分
c.r()
c.i()

()がダルい.

メソッド(関数)をフィールド(変数)の様に呼び出せれば、4回タイピング回数が減らせる.

()を無くそう(1/2)

先ほどのComplexクラスを以下のように変更

class Complex(real: Double, imaginary: Double) {
  def r = real
  def i = imaginary
}

diff

 class Complex(real: Double, imaginary: Double) {
-  def r() = real
-  def i() = imaginary
+  def r = real
+  def i = imaginary
 }

※sample/Complex2.scalaがソレです

()を無くそう(2/2)

ComplexNumbersも以下のように変更

object ComplexNumbers {
  def main(args: Array[String]) {
    val c = new Complex(1.2, 3.4)
    println("real part     : " + c.r)
    println("imaginary part: " + c.i)
  }
}

diff

-    println("real part     : " + c.r())
-    println("imaginary part: " + c.i())
+    println("real part     : " + c.r)
+    println("imaginary part: " + c.i)
   }

実行

書けたらComplex.scalaの在るディレクトリで以下のコマンドを実行.

scalac Complex.scala
scala ComplexNumbers
// real part     : 1.2
// imaginary part: 3.4

実行できましたか?

解説

引数の無いメソッドは括弧を省略できる.

次のメンドイところ

object ComplexNumbers {
  def main(args: Array[String]) {
    val c = new Complex(1.2, 3.4)
    println("real part     : " + c.r)
    println("imaginary part: " + c.i)
  }
}

    println("real part     : " + c.r)
    println("imaginary part: " + c.i)

いちいち文字列構築するの面倒くさい

toStringでキメる

インスタンスの内容をいちいち文字列を構築して表示するのは面倒なので、toStringに任せたい.

しかし…

val c = new Complex(1.2, 3.4)
println(c.toString)

を実行すると以下の様な結果になる.

Complex@625dcec6

意味わからん!!

継承とOverride

スーパークラスを指定していない場合、Scalaではscala.Objectというクラスが勝手にスーパークラスになる.


scala.ObjectのtoString()はクラス名@ハッシュで人間に分かり難い文字列が返ってくるので、適切にoverrideしよう!

toStringを良い感じにしよう(1/2)

Complexクラスを以下のように変更

class Complex(real: Double, imaginary: Double) {
  def r = real
  def i = imaginary
  // r± ai 的な感じで表示する.
  override def toString() = f"$r%f$i%+fi"
}

diff

+  // r ± ai 的な感じで表示する.
+  override def toString() = f"$r%f$i%+fi"
※ぶっちゃけ魔術

toStringを良い感じにしよう(2/2)

ComplexNumbersも以下のように変更

object ComplexNumbers {
  def main(args: Array[String]) {
    val c = new Complex(1.2, 3.4)
    println(c.toString)
  }
}

diff

-    println("real part     : " + c.r)
-    println("imaginary part: " + c.i)
+    println(c.toString)

実行

書けたらComplex.scalaの在るディレクトリで以下のコマンドを実行.

scalac Complex.scala
scala ComplexNumbers
// 1.200000+3.400000i

実行できましたか?

解説

スーパークラスのメソッドのオーバーライドにはoverrideキーワードが必ず必要

不用意なオーバーライドを避けるためです.

さて

ここからが本当のScalaだ!

jigoku

まだ面倒なところ

newがメンドイ

new Complex(1.2, 3.4)
new Complex(1.2, -3.4)

アクセッサーの定義がメンドイ

class Complex(real: Double, imaginary: Double) {
  def r = real //ここと
  def i = imaginary //ここ
}

倒すべき

1. newがメンドイ

2. アクセッサーの定義がメンドイ

それケースクラスで解決出来ます!!

その前に以下のことを解説します

  1. コンパニオンオブジェクト
  2. applyメソッド

コンパニオンオブジェクト

クラス定義と同一のファイルに同一の名前で定義したobject

// コンパニオンクラス
class Complex(real: Double, imaginary: Double) {
  def r = real
  def i = imaginary
  override def toString() = f"$r%f$i%+fi"
}
// コンパニオンオブジェクト
object Complex {
}

applyメソッド

言葉で説明するよりコードで!

applyは特別なメソッドで以下のように書くと

object Complex {
  def apply(i: Double, r: Double) = {
    println(f"$r%f$i%+fi")
  }
}
Complex.apply(1.2, 3.4)

の変わりに

Complex(1.2, 3.4)

と書ける!!

??????

前回やった

val list = List(1, 2, 3, 4)


とかも実は

val list = List.apply(1, 2, 3, 4)

を呼び出しているのです.

簡単でしょ?

コンパニオンオブジェクト & applyメソッドは原則としてインスタンスを生成するためのファクトリメソッドとして使用します

// コンパニオンクラス
class Complex(real: Double, imaginary: Double) {
  def r = real
  def i = imaginary
  override def toString() = f"$r%f$i%+fi"
}

// コンパニオンオブジェクト
object Complex {
  def apply(i: Double, r: Double) = {
      new Complex(i, r)
  }
}
// val c = Complex(1.2, 3.4)と書ける

解説

コンパニオンオブジェクトでファクトリメソッドを作れば、インスタンス化時にnewが要らなくなる.


val c = new Complex(1.2, 3.4)

val c = Complex(1.2, 3.4)

と書ける

newが要らなくなりました!!

1. newがメンドイ (倒した)

2. アクセッサーの定義がメンドイ

でも…

コード量がメッチャ増えた


punpun

新たな敵が…

1. newがメンドイ (倒した)

2. アクセッサーの定義がメンドイ

3. コンパニオンオブジェクト書くのダルい

それケースクラスで解決できるよ!

今度こそケースクラスです

説明しよう!

ケースクラスとは以下の機能がモリモリなクラスのことだ!!


  1. コンストラクタの引数をpublic val(フィールド)にする
  2. 上記フィールドでtoStringを自動的に実装
  3. コンパニオンオブジェクトを自動的に実装
  4. applyメソッドも自動的に実装
  5. 他にも色々自動でやってくれるが触れていない言葉が出てくるので省略

ケースクラスを使うと

class Complex(real: Double, imaginary: Double) {
  def r = real
  def i = imaginary
}
object Complex {
  def apply(i: Double, r: Double) = { new Complex(i, r) }
}

case class Complex(r: Double, i: Double)

と書ける…

_人人人人人人人_
> 突然の1行 <
 ̄Y^Y^Y^Y^Y^Y ̄

REPLでやってみよう

scala> case class Complex(r: Double, i: Double)
defined class Complex

scala> val c = Complex(1.2, 3.4)
c: Complex = Complex(1.2,3.4)

scala> c.r
res0: Double = 1.2

scala> c.i
res1: Double = 3.4

scala> c.toString
res2: String = Complex(1.2,3.4)

toStringは自動生成されますが、

c.toString
// Complex(1.2, 3.4)

という感じ(コンストラクタっぽく)に生成されますので、気に喰わない場合は以下のようにoverrideしてください

case class Complex(r: Double, i: Double) {
  override def toString() = f"$r%f$i%+fi"
}

ということで

一気に全部倒した

1. newがメンドイ

2. アクセッサーの定義がメンドイ

3. コンパニオンオブジェクト書くのダルい

caseクラス 凄い!!

おわり