Scalaのクラスとオブジェクト

クラス

シングルトンとは、そのクラスのインスタンスが絶対に1つである事を保証することです。
classキーワードで定義すると、シングルトンではないいわゆる普通のクラスになります。
以下のようにPerson型の変数を何個も作ることができます。

class Person(name: String) {
  def greet(): Unit = println(s"私の名前は${name}です。")
}

scala> val taro = new Person("太郎")
taro: Person = Person@3f476f5d

scala> val hanako = new Person("花子")
hanako: Person = Person@39b94367

scala> taro.greet
私の名前は太郎です。

scala> hanako.greet
私の名前は花子です。

オブジェクト

オブジェクトは、シングルトンであることが約束されています。 以下だと、JVMの中で唯一Robot型のオブジェクトになります。
Scalaでは、クラスでstaticなメソッドやフィールドは定義できない(staticキーワード自体が無い)ため、代わりにオブジェクトを使用します。

object Robot {
  var name = "ロボ太"
  def greet(): Unit = println(s"コンニチハ、${name}デス。")
  def add(x: Int, y: Int): Unit = println(s"コタエハ${x + y}デス。")
}

scala> Robot.greet
コンニチハ、ロボ太デス。

scala> Robot.name = "ロボ子"
Robot.name: String = ロボ子

scala> Robot.greet
コンニチハ、ロボ子デス。

scala> Robot.add(1,3)
コタエハ4デス。

オブジェクトは、自動でインスタンスが生成されるため、明示的にインスタンス生成はできません。もちろんコンストラクタ引数を定義することもできません。

クラスを継承したオブジェクト

クラスは、newでインスタンス化するほかに、オブジェクトに継承させて利用することもできます。

class Person(name: String) {
  def greet(): Unit = println(s"私の名前は${name}です。")
}

scala> object Taro extends Person("太郎")
defined object Taro

scala> Taro.greet
私の名前は太郎です。

applyメソッド

オブジェクト内のapplyメソッドはオブジェクト名(applyメソッドの引数)で呼び出せるようになっています。
以下のようにnewを使用せずにインスタンス生成できます。
またScalaのクラスは、基本的に1つしかコンストラクタを持たないようですが、applyメソッドを使ってコンストラクタを増やすことができます。

class Robot(name: String) {
  def greet(): Unit = println(s"コンニチハ、${name}デス。")
}

object Robot {
  def apply(name: String) = new Robot(name)
  def apply(id: Int) = new Robot(s"ID${id}番")
}

scala> val robota = Robot("ロボ太")
robota: Robot = Robot@5daf8f75

scala> robota.greet
コンニチハ、ロボ太デス。

scala> val robot1 = Robot(1)
robot1: Robot = Robot@16932328

scala> robot1.greet
コンニチハ、ID1番デス。

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

RobotオブジェクトとRobotクラスのように同じ名前であれば、それぞれコンパニオンオブジェクト、コンパニオンクラスと呼びます。
コンパニオンオブジェクトとコンパニオンクラスは、互いにprivateなメンバーにアクセスできます。

class Robot(private val name: String) {
  def greet(): Unit = println(s"コンニチハ、${name}デス。")
}

object Robot {
  def getName(robot: Robot): String = robot.name
}

scala> val robota = new Robot("ロボ太")
robota: Robot = Robot@5243ab3b

scala> robota.name
<console>:13: error: value name is not a member of Robot

scala> Robot.getName(robota)
res1: String = ロボ太

なお以下のように、コンストラクタ引数にprivate valを付けない場合はエラーになります。
引数に何も付けないと、暗黙的にprivate valのフィールドになるとのことですが、ちゃんと省略せずに定義したフィールドとは振る舞いが異なる?

class Robot(name: String) {
  def greet(): Unit = println(s"コンニチハ、${name}デス。")
}

object Robot {
  def getName(robot: Robot): String = robot.name
}
<pastie>:16: error: value name is not a member of Robot