Scalaのクラスについて


## クラスの宣言、コンストラクタ

##### 宣言とインスタンス化

シンプルなクラス宣言

```scala:SimpleClass.scala
/** class and instance sample */
class SimpleClass {
}
```

*new* でインスタンス化できる。

>
scala> val hoge = new SimpleClass
hoge: SimpleClass = SimpleClass@51ea07a3

##### 基本コンストラクタ

デフォルトコンストラクタでの処理はクラス内に直接記載すればよい。

```scala:SimpleClass.scala
/** class and instance sample */
class SimpleClass {
  // インスタンス化のタイミングで実行される。
  println("create SimpleClass") 
}
```

>
scala> val hoge = new SimpleClass
create SimpleClass
hoge: SimpleClass = SimpleClass@5db2381b

コンストラクタが引数をもつ場合は、宣言時にクラス名の後ろに引数を定義する。

```scala:SimpleClass.scala
/** class and instance sample */
class SimpleClass(id:String) {
  // インスタンス化のタイミングで実行される。
  println("create SimpleClass id:" + id)
}
```

>
scala> val hoge = new SimpleClass("test")
create SimpleClass id:test
hoge: SimpleClass = SimpleClass@40ab627a

基本コンストラクタが引数有りなので、引数なしではインスタンス化できない。
>
scala> val hoge = new SimpleClass
<console>:7: error: not enough arguments for constructor SimpleClass: (id: String)SimpleClass.
Unspecified value parameter id.
       val hoge = new SimpleClass


##### 補助コンストラクタ

基本コンストラクタ以外のコンストラクタを定義する場合は、 *sub this(...)* で定義する。
補助コンストラクタは、必ず始めに基本コンストラクタを呼ばないといけない。

```scala:SimpleClass.scala
/** class and instance sample */
class SimpleClass(id:String) {
  // インスタンス化のタイミングで実行される。
  println("create SimpleClass id:" + id)

  // 補助コンストラクタ
  def this() {
    // 先頭で基本コンストラクタを呼び出す必要がある。
    this("dummy");
    println("create by sub contracter")
  }
}
```

>
scala> val piyo = new SimpleClass
create SimpleClass id:dummy
create by sub contracter
piyo: SimpleClass = SimpleClass@5babd8cb

## メソッドの宣言

##### メソッド宣言の基本
*def メソッド名(引数):戻り値* でメソッドを宣言する。ドット指定で呼び出せる。

```scala:SimpleClass2.scala
/** method sample */
class SimpleClass2 {

  def sayHello(name:String):Unit = {
    println("hello, " + name + "!!");
  }
}
```

>
scala> val hello = new SimpleClass2
hello: SimpleClass2 = SimpleClass2@75bc45de
>
scala> hello.sayHello("taro")
hello, taro!!

##### オーバーロード
引数の数、型を変えることで同名のメソッドを定義できる。

```scala:SimpleClass2.scala
/** method sample */
class SimpleClass2 {

  def sayHello(name:String):Unit = {
    println("hello, " + name + "!!");
  }

  // 引数が異なればオーバーロード可能
  def sayHello(times:Int):Unit = {
    println("hello," * times);
  }
  def sayHello(name:String, times:Int):Unit = {
    println(( "hello," * times ) + " " + name + "!!");
  }

  // 戻り値が異なるメソッドは定義できない。
  /* 以下はコンパイルエラーになる。
  def sayHello(times:Int):String = {
    "hello" * times;
  }
  */
}
```

>
scala> val hello = new SimpleClass2
hello: SimpleClass2 = SimpleClass2@2448b7f
>
scala> hello.sayHello("jiro")
hello, jiro!!
>
scala> hello.sayHello(3)
hello,hello,hello,

##### 名前付き引数
引数名を指定して呼び出すことで、定義順を無視して引数を指定することができる。

>
scala> hello.sayHello("hoge",3)
hello,hello,hello, hoge!!
>
scala> hello.sayHello(times=3, name="hoge")
hello,hello,hello, hoge!!

##### 引数のデフォルト値

```scala:SimpleClass2.scala
/** method sample */
class SimpleClass2 {

  def sayHello(name:String):Unit = {
    println("hello, " + name + "!!");
  }

  // 引数が異なればオーバーロード可能
  def sayHello(times:Int):Unit = {
    println("hello," * times);
  }
  def sayHello(name:String, times:Int):Unit = {
    println(( "hello," * times ) + " " + name + "!!");
  }

  // 戻り値が異なるメソッドは定義できない。
  /* 以下はコンパイルエラーになる。
  def sayHello(times:Int):String = {
    "hello" * times;
  }
  */

  // 引数にデフォルト値を定義
  def sayBye(name:String, times:Int = 3):Unit =  {
    println( ("Bye,"*times) + " " + name + "!!" );
  }
}
```

引数にデフォルト値を定義することで、メソッド呼び出し時に引数を省略することができる。

>
scala> hello.sayBye("tanaka", 2)
Bye,Bye, tanaka!!
>
scala> hello.sayBye("tanaka")
Bye,Bye,Bye, tanaka!!

##### 暗黙の引数

引数の型に *implicit* を書くと、暗黙の引数となる。
引数を省略した際に、呼び出し先のスコープにて *implicit* 付きで定義された変数が暗黙的に設定される。

```scala:SimpleClass2.scala
/** method sample */
class SimpleClass2 {

  def sayHello(name:String):Unit = {
    println("hello, " + name + "!!");
  }

  // 引数が異なればオーバーロード可能
  def sayHello(times:Int):Unit = {
    println("hello," * times);
  }
  def sayHello(name:String, times:Int):Unit = {
    println(( "hello," * times ) + " " + name + "!!");
  }

  // 戻り値が異なるメソッドは定義できない。
  /* 以下はコンパイルエラーになる。
  def sayHello(times:Int):String = {
    "hello" * times;
  }
  */

  // 引数にデフォルト値を定義
  def sayBye(name:String, times:Int = 3):Unit =  {
    println( ("Bye,"*times) + " " + name + "!!" );
  }

  // 暗黙の引数を定義
  def greet(implicit name:String):Unit = {
    println( "Hi! " + name );
  }
}
```

>
scala> val hello = new SimpleClass2
hello: SimpleClass2 = SimpleClass2@4e1f52a
>
scala> implicit val name = "tanaka"
name: String = tanaka
>
scala> hello.greet
Hi! tanaka
>
scala> hello.greet("sato")
Hi! sato

変数が定義されていないとコンパイルエラーになる。
>scala> val hello = new SimpleClass2
hello: SimpleClass2 = SimpleClass2@4e1f52a
>
scala> hello.greet
<console>:9: error: could not find implicit value for parameter name: String
              hello.greet
                    ^


## フィールドとアクセサ

##### フィールドの宣言

フィールドはクラス内に直接定義できる。

```scala:SimpleClass3.scala
/** field sample */
class SimpleClass3 {
  // 変更可能なフィールド
  var var_id:Int = 1;
  // 不変なフィールド
  val val_id:Int = 2;
  // 外部から参照できないフィールド
  private val private_id = 999;
}
```

外部からはフィールド名指定での参照・設定が可能。

>
scala> val hoge = new SimpleClass3
hoge: SimpleClass3 = SimpleClass3@10f8ed
>
scala> hoge.var_id
res0: Int = 1
>
scala> hoge.var_id = 3
hoge.var_id: Int = 3
>
scala> hoge.var_id
res1: Int = 3

不変なフィールドは参照のみ可能。
>
scala> hoge.val_id = 3
<console>:8: error: reassignment to val
       hoge.val_id = 3
                   ^

privateなフィールドは参照も不可。
>
scala> hoge.private_id
<console>:9: error: value private_id in class SimpleClass3 cannot be accessed in SimpleClass3
              hoge.private_id
                   ^

##### アクセサ

フィールドへのアクセスは、暗黙的に生成されるアクセサを経由して行われている。
getterは変数名、setterは変数名_=(xxx)だが、変数名=xxxという形式でも呼び出せる。（こちらのほうが自然）

```scala:SimpleClass3.scala
/** field sample */
class SimpleClass3 {
  // 変更可能なフィールド
  var id:Int = _; // _ は型のデフォルト値
}
```

>
scala> val hoge = new SimpleClass3
hoge: SimpleClass3 = SimpleClass3@6143c610
>
scala> hoge.id = 3
hoge.id: Int = 3
>
scala> hoge.id_=(999)
>
scala> hoge.id
res5: Int = 999

明示的にアクセサを定義する場合は、上記ルールに則ってメソッドを定義し、フィールドはprivateにしてアクセスを制約すればよい。
呼び出し元に変化がないところがポイント

```scala:SimpleClass3.scala
/** field sample */
class SimpleClass3 {
  // アクセサを独自実装するため、privateとする。
  private var _id:Int = _; // _ は型のデフォルト値

  // getter
  def id:Int = {
    println("call getter!");
    _id;
  }
  // setter
  def id_= (id:Int) = {
    println("call setter!");
    _id = id;
  }
}
```

>
scala> val hoge = new SimpleClass3
hoge: SimpleClass3 = SimpleClass3@16becf68
>
scala> hoge.id = 3
call setter!
call getter!
hoge.id: Int = 3
>
scala> hoge.id_=(999)
call setter!
>
scala> hoge.id
call getter!
res1: Int = 999


## 継承

## シングルトン

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

## ケースクラス