新人SEの学習記録

14年度入社SEの学習記録用に始めたブログです。もう新人じゃないかも…

学習記録:ドワンゴ 新人向けScala研修テキスト

参考文献

dwango.github.io

8. オブジェクト

Scalaでは全ての値がオブジェクトであり,全てのメソッドは何らかのオブジェクトに所属している。
そのため,Javaのようにクラスに属するstaticフィールド/メソッドを作成することはできない。
その代わりといっては語弊があるが,objectキーワードによって,同じ名前のシングルトンオブジェクトをグローバルな名前空間に1つ定義することができる。

objectの基本構文はクラスとほとんど同じである。

object オブジェクト名 extends クラス名 with トレイト名1 with トレイト名2 .... {
  本体
}

object構文の主な用途には,以下の3つが挙げられる。(もっぱら最初の2つの用途で使われる)

Scalaでは標準でPredefというobjectが定義・インポートされており,これは最初の使い方に当てはまる。
println()などのメソッドも実はPredef objectのメソッドになる。

2番目の使い方の例として,点を表すPointクラスのファクトリをobjectで作ろうとすると次のようになる。

scala> class Point(val x:Int, val y:Int)
defined class Point

scala> object Point {
     |   def apply(x: Int, y: Int): Point = new Point(x, y)
     | }
defined object Point

applyという名前のメソッドは処理系で特別に扱われ,Point(x)のような記述は(Point objectにapplyメソッドが定義されていれば)Point.apply(x)と解釈される。
これを利用することで,Point(3, 5)のような記述でオブジェクトを生成できるようになる。

scala> Point(3, 5)
res27: Point = Point@30ddf308

これは,new Point()で直接Pointオブジェクトを生成するのに比べ,クラスの実装詳細を内部に隠しておける,Pointではなくそのサブクラスのインスタンスを返すことができる,といったメリットがある。

なお,上記の記述は,ケースクラスを用いてもっと簡単に書ける。
ケースクラスの詳細は後述するが,簡単に言うと,case classを付けたクラスの全てのフィールドを公開し,equels(), hashCode(), toString()などの基本的なメソッドを持ったクラスを生成し,またそのクラスを生成するためのファクトリメソッドを生成する。

scala> case class Point(x: Int, y: Int)
defined class Point

scala> Point(5, 3)
res29: Point = Point(5,3)

scala> println(res29)
Point(5,3)

scala> Point(1, 2).equals(Point(1, 2))
res31: Boolean = true

scala> Point(1, 2).equals(Point(2, 3))
res32: Boolean = false

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

クラス名と同じ名前のシングルトンオブジェクトは,コンパニオンオブジェクトと呼ばれる。
コンパニオンオブジェクトは,対応するクラスに対して特権的なアクセス権を持つ。

// ageをprivateに
class Person(private val age: Int)

object Hoge {
  val taro = new Person(13)
  // privateなのでアクセスできない
  println(taro.age)
}

// Personのコンパニオンオブジェクト
object Person {
  val taro = new Person(13)
  // アクセスできる
  println(taro.age)
}

なお,コンパニオンオブジェクトでもprivate[this]なメンバに対してはアクセスできない。

上記のようなコンパニオンオブジェクトを使ったコードをREPLで試す場合は,REPLの:pasteコマンドを使い,クラスとコンパニオンオブジェクトを一緒に貼り付ける必要がある。
(クラスとコンパニオンオブジェクトは同一ファイルに置かれている必要があり,REPLで普通に入力した場合コンパニオン関係を認識できないため)

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

class Person(private val age: Int)

object Person {
  val taro = new Person(20)
  println(taro.age)
}

// Exiting paste mode, now interpreting.

defined class Person
defined object Person

scala> Person
20
res33: Person.type = Person$@3918a738