【備忘録】Scala入門

# 前提
Scala2系です。
3系だとパッケージオブジェクトとかtraitのwithとか違うと思います

# 暗黙の了解
Scalaのファイル名はアッパーキャメルケースを使用します。(Main.Scala・Index.Scala・・・)

## オブジェクト
オブジェクト:状態・処理を保持できる機構
### 構文
```Scala:Main.Scala
// Mainというオブジェクトを作成する
object Main
```

## トレイト
トレイト:振る舞いを定義するもの
ミックスイン:トレイトで定義した振る舞いをオブジェクトに追加すること
```Scala:Main.Scala
// AppトレイトをMainオブジェクトにミックスインしている
object Main extends App { }
```

### 変数・定数
**変数:** `var`を使う
**定数:** `val`を使う
構文↓
```Scala
object Repl extends App {
  var hour = 24;  // 変数
  val minute = 60;  // 定数

  hour = 12;  // OK
  minute = 3600;  //NG
}
```

### テンプレートリテラル
文字列の前に`s`、文字列中の変数に`${変数名}`を与える
構文↓
```Scala
object Repl extends App {
  val hour = 24;
  println(s"1日は${hour}時間です。") // 1日は24時間です。
}
```

### 関数定義
`def 関数名(引数名: 型): 戻り値の型 = { }`
例↓
```Scala
object Repl extends App {
  def callMyName(name: String) = {
    println(s"私の名前は${name}です。")
  }
  
  callMyName("warabimochi")  // 私の名前はwarabimochiです。
}

```
### 可変長引数の定義・呼び出し
可変長引数の定義は引数の型に`*`を付ける
配列・シーケンスを渡したい時は`: _*`を付けて展開してわた

```scala
object Repl extends App {
  def kahentyouhikisuu(nums: Int*): Unit = {
    println(nums)
  }

  kahentyouhikisuu(1, 2)  // ArraySeq(1, 2)
  kahentyouhikisuu(Array(1, 2, 3 ,4): _*)  // ArraySeq(1, 2, 3, 4)
}
```

### 関数呼び出し
引数が無い関数の呼び出しは()を省いても良い
例↓
```scala
object Repl extends App {
  def temp() = {
    println("temp関数が呼び出されました。")
  }
  
  temp // temp関数が呼び出されました。
}
```
### シーケンス(Seq)
Scalaで良く使う使いやすいメソッドを持つ配列
シーケンスは任意の添え字の値を変更出来ないので更新時は`:+`を使う
```scala
object Repl extends App {
  val sequence = Seq(1, 2, 3)
  println(sequence)  // List(1, 2, 3)
  println(sequence(0))  // 1

  val updatedSequence = sequence :+ 4
  println(updatedSequence)  // List(1, 2, 3, 4)

  sequence(0) = 4  // NG
}
```

### マップ(map)
Scalaの連想配列
マップは不変なので更新時は`map変数 + Map()`を使う
```scala
object Repl extends App {
  val map = Map("key1" -> 1, "key2" -> 2)
  println(map)  // Map(key1 -> 1, key2 -> 2)
  println(map("key1"))  // 1

  val updatedMap = map + ("key3" -> 3)
  println(updatedMap)  // Map(key1 -> 1, key2 -> 2, key3 -> 3)
}
```

### String
**String:**`""`で囲むと文字列になる
`String変数(添え字)`で文字を抜き出せる
```scala
object Repl extends App {
  val str = "abcde"
  println(str(0))  // "a"
  println(str.getClass())  // java.lang.String
}
```

### Char
**Char:**`''`で囲むと文字になる
```scala
object Repl extends App {
  val char = 'a'
  println(char.getClass)  // char
}
```

### if式
ifも式なので値を返せる
else句を書かないが、条件式が`false`だった場合`Unit`が返る
```scala
object Repl extends App {
  var flag = true
  val trueReturn = if(flag) {
    "flag:true"
  }
  println(trueReturn)  // flag:true

  flag = false
  val falseReturn = if(flag) {
    "flag:false"
  }
  println(falseReturn)  // ()
}
```

### for式
Range型の値を複数for式に渡すとfor式一つで多重ループを実現できる
for式にif句を渡すとその条件の時だけループを回すフィルタリングが可能
for式に渡すループ条件はシーケンスとか配列も渡せる
```scala
object Repl extends App {
  for(i <- 0 to 10) {
    println(i)  // 0, 1, 2... 10
  }

  for(i <- 0 until 10) {
    println(i)  // 0, 1, 2... 9
  }

  for(i <- 10 to 0 by -1) {
    println(i) // 10, 1, 2... 0
  }

  for(i <- 0 to 2; j <- 0 to 2) {
    println(s"i = ${i} j = ${j}")  // i=0 j=0, i=0 j=1... i=2 j=2
  }
  
  for(i <- 0 to 2; j <- 0 to 2; if i == 0) {
    println(s"i=${i} j=${j}")  // i=0 j=0, i=0 j=1... i=0 j=3
  }

  for(i <-  Seq("A", "B", "C", "D", "E")) {
    println(i)  // A, B, C... E
  }

  for(i <- Seq("A", "B", "C", "D", "E")) yield {
    s"これは${i}です"  // List("これはAです", "これは"Bです"... "これはEです"
  }
}
```

### while文
正確にはwhile式、こっちは一般的な感じ
式ではあるが常に`Unit`しか返さないので文として使われる
```scala
object Repl extends App {
  var i = 0;
  while(i <= 10) {
    println(i)  // 0, 1, 2... 10
    i = i + 1
  }
}
```

### タプル(Tuple)・分割代入
**タプル:**`(値1, 値2...)`で書くやつ
`(変数1, 変数2...)`とすることでタプルを変数に分割代入出来る
```scala
object Repl extends App {
  val (num1, num2) = (1, 2)
  
  println(num1)  // 1
  println(num2)  // 2
}
```

### {}式
ブロックで囲まれた式は最後の式が評価された値が返る
```scala
object Repl extends App {
  val branceReturn = {
    println("A")
    1 + 2
  }
  println(branceReturn)  // branceReturn:3
}
```

### match式・パターンマッチ
**match式:** JavaScriptのswitch文みたいなモノ
case句に`_`を使うと**ワイルドカード**になる
case句に`|`を使うと複数個指定できる
case句に`Seq`・`Array`等を渡すと要素を**束縛**して取り出せる
case句に`変数名:型`を渡すと型で**パターンマッチ**が行える
```scala
object Repl extends App {
  val result = 3 match {
    case 1 => "Java"
    case 2 => "Scala"
    case _ => "Kotlin"
  }

  println(result)  // kotlin

  def matchFunc(str: String) = {
    str match {
      case "good" | "good2" => {
        println("引数はgoodかgood2です。")
      }
    }
  }
  
  matchFunc("good")  // 引数はgoodかgood2です。
  matchFunc("good2")  // 引数はgoodかgood2です。

  val sequence = Seq(1, 2, 3, 4, 5)

  // コレクションの束縛
  sequence match {
    case Seq(i1, i2, i3, i4, i5) =>
      println(i1, i2, i3, i4, i5)  // 1, 2, 3, 4, 5
  }

  // ifガード句を使った束縛
  sequence match {
    case Seq(i1, i2, i3, i4, i5) if i1 != 1 => {
      println(i1, i2, i3, i4, i5)
    }
    case _ => {
      println("該当するケースが存在しませんでした。")  // 該当するケースが存在しませんでした。
    }
  }

  // asパターンでの束縛
  sequence match {
    case i@Seq(1, 2, 3, 4, 5) => {
      println(i)  // List(1, 2, 3, 4, 5)
    }
  }

  val str: AnyRef = "文字列"
  str match {
    case i: Integer => println(s"${str}はint型です。")
    case i: String => println(s"${str}はstring型です。")  // 文字列はstring型です。
  }
}
```

### class
**クラス:** インスタンスを作る雛形
**インスタンス:** クラスから作られたオブジェクト
- **フィールド:** クラスが持つ**状態**
- **メソッド:** クラスが持つ**振る舞い**
- **メンバー:** **フィールド**と**メソッド**を合わせた呼び名

**コンストラクタ:** オブジェクトの生成と初期化を同時に行う時に利用するメソッド
コンストラクタ引数に`val`・`var`を付けることで外部にフィールドを公開する
**カプセル化:** メソッドに`private`を付けると外部に公開しない
**抽象クラス:** `abstract`を付けた継承するために使うクラス・インスタンス化出来ない
```scala
object Repl extends App {
  class NormalCat(name: String) {
    def greet(): Unit = println(s"僕、${name}")
  }
  
  val normalCat = new NormalCat("コンストラクタ引数がノーマルな猫")
  normalCat.greet()  // 僕、コンストラクタ引数がノーマルな猫
  println(normalCat.name)  // Error:value name is not a member of Repl.NormalCat

  class ValCat(val name: String) {
    def greet(): Unit = println(s"僕、${name}")
  }

  val valCat = new ValCat("コンストラクタ引数がvalな猫")
  valCat.greet()  // 僕、コンストラクタ引数がvalな猫
  println(valCat.name)  // コンストラクタ引数がvalな猫


  class PrivateCat(name: String) {
    private def greet(): Unit = println(s"僕、${name}")
  }
  val privateCat = new PrivateCat("メソッドがprivateな猫")
  privateCat.greet()  // Error


  class NormalClass() {
    println("normalClass")
  }
  val normalClass = new NormalClass()  // normalClass

  abstract class AbstractClass() {
    println("abstractClass")
  }
  val abstractClass = new AbstractClass()  // Error


}
```

### 継承(敢えてclassとは別に書きます)
**継承**とは**継承元クラス**の`メンバー`を**継承先クラス**でも使えるようにする機能のこと
具体的には**継承先クラス** `extends` **継承元クラス(`コンストラクタ引数`)** と書く
**継承元クラス**に`コンストラクタ引数`が存在しない場合は、継承時に`()`を省く
**継承元クラス**に`コンストラクタ引数`が存在する場合は、継承時に`コンストラクタ引数`を渡す。
**オーバーライド:** **継承元クラス**に存在する`メソッド`を **継承先クラス**に同じく定義することで、**継承元クラス**の`メソッド`の挙動を上書きすること
具体的には`override def`のように`override`を前に付ける
```scala
object Repl extends App {
  abstract class OyaClass {
    val str = "親クラス1フィールドの文字列"
    def funcTemp() = println("親クラス1のfuncTempが実行されました。")
  }
  class KoClass extends OyaClass

  abstract class OyaClass2(str: String) {
    val str2 = "親クラス2フィールドの文字列"
    def funcTemp() = println("親クラス2のfuncTempが実行されました。")
  }

  class koClass2 extends OyaClass2("コンストラクタ引数が必要")


  abstract class Parent {
    def callClassName = println("このクラスはParentクラスです。")
  }

  class Child extends Parent {
    override def callClassName = println("このクラスはChildクラスです。")
  }

  val child = new Child()
  child.callClassName  // このクラスはChildクラスです。
}
```

### object
`object`を使って作成した**オブジェクト**は、**シングルトンオブジェクト**になる
なので、**インスタンス**生成が不要
**シングルトンオブジェクト:** インスタンスが1つしか存在しないオブジェクト

`object`に`class`を継承することで、`new`を使わなくても、**インスタンス生成**が可能


`object`の`apply`メソッドは **object名(引数)** で呼び出せる
なので、**コンパニオンオブジェクト**を作成し、
`apply`メソッドに**ファクトリメソッド**を作ることが多い
**コンパニオンオブジェクト:** **class名**と同名の`object`
**ファクトリメソッド:** インスタンスを生成する戻り値として返すメソッド

**コンパニオンオブジェクト**の中からなら同名`class`の`private`なメンバーを参照できる

以下の**ケース**で使う
- **グローバル変数**・**ユーティリティーメソッド**
- `new`を使わない**インスタンス生成**
- **コンパニオンオブジェクト**・**ファクトリメソッド**

```scala
object SingletonObject {
  var name = "warabimochi"
  private val score = 100

  def getScore() = score
}

object Repl extends App {
  println(SingletonObject.name)  // warabimochi
  SingletonObject.name = "warabimochi2"
  println(SingletonObject.name)  // warabimochi2
  println(SingletonObject.getScore())  // 100
  println(SingletonObject.score)  // Error


  class ParentClass {
    def printParentClassName = println("このクラスの名前はParentClassです。")
  }

  object SingletonObject2 extends ParentClass
  SingletonObject2.printParentClassName  // このクラスの名前はParentClassです。


  object Temp {
    def apply(name: String) = println(s"引数${name}でapplyメソッドが実行されました。")
  }

  Temp("warabimochi")  // 引数warabimochiでapplyメソッドが実行されました。

  
  class Dog(private val name: String) {
    def printClassName = println(s"引数:${name} Dogクラスがprintされました。")
  }
  
  object Dog {
    def apply(name: String) = new Dog("name")
    def apply(num: Int) = new Dog(s"${num}番目の犬")
    def printName(dog: Dog) = println(dog.name)
  }

  val dogStr = Dog("犬")
  dogStr.printClassName  // 引数:name Dogクラスがprintされました。
  val dogNum = Dog(1)
  dogNum.printClassName  // 引数:1番目の犬 Dogクラスがprintされました。

  Dog.printName(dogStr)  // name
  println(dogStr.name)  // Error
}
```

### case class
**case class:** データ構造を**表現**するのに使われるクラスです。
一部のフィールドを更新したい時は`copy`メソッドを使用する
`case class名(引数1, 引数2...)` = `case class`のインスタンス で**分割代入**が可能
`case class`同士の比較は**フィールドの値が全て等しいかどうか**をチェックするため、
`class`とは同値性に差が存在する

以下が`case class`の機能です。
- `toString`メソッドの実装
- `コンパニオンオブジェクト`・`ファクトリメソッド`の実装
- **コンストラクタ引数**に`val`を与える
- **構造的等価性**の実装

```scala
object Repl extends App {
  case class CaseClassPoint(x: Int, y: Int)
  var caseClassPoint1 = CaseClassPoint(1, 2)  // CaseClassPoint(1,2)
  println(caseClassPoint1.x)  // 1
  println(caseClassPoint1.y)  // 2

  var caseClassPoint2 = caseClassPoint1.copy(x = 3)
  println(caseClassPoint2.x)  // 3
  println(caseClassPoint2.y)  // 2


  val CaseClassPoint(x, y) = caseClassPoint2
  println(x)  // 3
  println(y)  // 2

  class ClassPoint(x: Int, y: Int) {}
  val classPoint1 = new ClassPoint(1, 2)
  val classPoint2 = new ClassPoint(1, 2)
  println(s"同インスタンスの比較: ${classPoint1 == classPoint1}")  // true
  println(s"同フィールド異インスタンスの比較: ${classPoint1 == classPoint2}")  // false

  case class CaseClassPoint(x: Int, y: Int) {}
  val caseClassPoint1 = CaseClassPoint(1, 2)
  val caseClassPoint2 = CaseClassPoint(1, 2)
  println(s"同インスタンスの比較: ${caseClassPoint1 == caseClassPoint1}") // true
  println(s"同フィールド異インスタンスの比較: ${caseClassPoint1 == caseClassPoint2}")  // true
}
```

### sealed class
`sealed`をクラス宣言の前に付けると同ファイルでのみ継承が可能になる
```scala:Repl.scala
object Repl extends App {
  sealed class SealedClass() {}
  class Class() {}

  class SealedClass2 extends SealedClass() {}  // OK
  class Class2 extends Class() {}  // OK
}
```
```scala Temp.scala
import Repl.{Class, SealedClass}

object Temp {
  class SealedClass2 extends SealedClass() {}  // Error
  class Class2 extends Class() {}  // OK
}
```

### case object + sealed classで実現する漏れないパターンマッチ
以下の実装例では`case Female`が無いという指摘をコンパイラがしてくれるらしいが上手くいかない

`enum`っぽく使うらしい
`sealed class`で`case object`達をまとめている感覚でいるとよさそう
```scala
object Repl extends App {
  sealed abstract class HumanGender
  case object Male extends HumanGender
  case object Female extends HumanGender

  def printGender(gender: HumanGender) = gender match {
    case Male => 1
  }
}
```
### package
**package:** 論理的に同一ファイルとして扱う仕組み
-> 同一packageなら別ファイルでも`import`が必要ない

**package名**は**ディレクトリ名・ディレクトリ階層と一致させる
ex)`src/main/scala/temp/test.scala` -> `package temp`

**逆ドメイン方式**:`package`の命名規則
ex)`package jp.co.warabimochi.game`

**デフォルトパッケージ:** package宣言が無い時に所属するパッケージのこと
デフォルトパッケージ同士は同パッケージと同じ扱いになる

**パッケージオブジェクト:** パッケージのトップレベルに存在する物を定義出来るオブジェクト
オブジェクトの中身は同一パッケージであればそのまま参照できる
-> **定義方法:** **パッケージ名**と**オブジェクト名**を同一にする

```scala:src/main/scala/test/testRepl.scala
package test

object testRepl extends App {
  testRepl2.printMyObjectName()  // 私のオブジェクト名はtestRepl2です。
  printMyPackageObjectName()  // 私はパッケージオブジェクトのtestです。
}
```
```scala:src/main/scala/test/testRepl2.scala
package test

object testRepl2 extends App {
  def printMyObjectName() = println("私のオブジェクト名はtestRepl2です。")
  printMyPackageObjectName()  // 私はパッケージオブジェクトのtestです。
}
```
```scala:src/main/scala/test/package.scala
package object test {
  def printMyPackageObjectName() = println("私はパッケージオブジェクトのtestです。")
}
```
**import:** `package`を跨いで**クラス・オブジェクト・メソッド**を読み込む時に使う
**単一のimport**: `import パッケージ名.クラス名`
**複数のimport**: `import パッケージ名.{クラス名1, クラス名2...}`
**package内全import:** `import パッケージ名._`

```scala:src/main/scala/test2/Repl.scala
package test2

import test.testRepl2

object Repl extends App {
  testRepl2.printMyObjectName()  // 私のオブジェクト名はtestRepl2です。
}
```

### trait
**トレイト:** `クラス`に対して**動作**をくっつけて増やすもの
**ミックスイン:** トレイトを継承すること

- トレイトのミックスインには個数制限がない
1つ目の継承 or ミックスインは `extends`を使うが、2つ目以降は`with`で繋げる

- トレイトは直接インスタンス生成できない
**匿名内部クラス:** `new トレイト名 {}` で**トレイトをミックスイン**した別のクラスを作ること
これを使うとトレイトのインスタンス生成が可能

- トレイトにはコンストラクタ引数がない

```scala
trait Drink {
  def drink(): Unit = println("飲む")
}

trait Eat {
  def eat(): Unit = println("食べる")
}

class Repl
class RealRepl extends Repl with Drink with Eat
object Main extends App {
  val realRepl = new RealRepl
  realRepl.drink()  // 飲む
  realRepl.eat()  // 食べる

  val drink = new Drink {}
  drink.drink()  // 飲む
}
```

**菱形継承問題:** 複数ミックスインしたトレイト同士が同名のメソッドを持っていた場合
子クラスでそのメソッドを実行する際にどちらを実行すべきか分からないこと
- 解決策①:子クラスでメソッドを`override`する
```scala
trait Drink {
  def drink(): Unit = println("飲む")
  def eat(): Unit = println("ドリンクtraitだけど食べる")
}

trait Eat {
  def eat(): Unit = println("食べる")
}

class Repl
class RealRepl extends Repl with Drink with Eat
class RealRepl2 extends Repl with Drink with Eat {
  override def eat(): Unit = println("RealRepl2クラスでeat関数を呼ぶ")
}

object Main extends App {
  val realRepl = new RealRepl
  realRepl.eat()  // Error

  val realRepl2 = new RealRepl2
  realRepl2.eat()  // RealRepl2クラスでeat関数を呼ぶ
}
```
- 解決策②:親トレイトの抽象メソッドを子トレイトのメソッドで`override`させて**線形化**で解決する
**線形化:** トレイトの継承が直列で行われることで、`override`が重なって解決されること
```scala
trait ParentTrait {
  def callMyTraitName(): Unit
}

trait Trait1 extends ParentTrait {
  override def callMyTraitName(): Unit = println("私のトレイト名はTrait1です。")
}

trait Trait2 extends ParentTrait {
  override def callMyTraitName(): Unit = println("私のトレイト名はTrait2です。")
}

class ClassA extends Trait1 with Trait2
class ClassB extends Trait2 with Trait1
object Main extends App {
  val instance1 = new ClassB  // Trait1
  val instance2 = new ClassA  // Trait2

  instance1.callMyTraitName()  // 私のトレイト名はTrait1です。
  instance2.callMyTraitName()  // 私のトレイト名はTrait2です。
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/023246b2-0c31-4ab3-a14c-5af9b084907e.png)

単一の多段継承時、`super`は親トレイトを指す
```scala
trait ParentTrait {
  def printMyTraitName(): Unit = println("ParentTrait: hoge")
}

trait TraitA extends ParentTrait {
  override def printMyTraitName(): Unit = {
    super.printMyTraitName()
    println("hoge")
  }
}

class Class1 extends TraitA
object Main extends App {
  val instance = new Class1

  instance.printMyTraitName()
  // ParentTrait: hoge
  // hoge
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/f55e8f36-b793-4fe3-a060-5aaf2204d24e.png)


親トレイトを複数の子トレイトが継承する時、`super`は親トレイトを指す
```scala
trait ParentTrait {
  def hoge(): Unit = println("ParentTrait: hoge")
}

trait TraitA extends ParentTrait {
  override def hoge(): Unit = {
    super.hoge()
    println("hoge")
  }
}

trait TraitB extends ParentTrait {
  override def hoge(): Unit = {
    super.hoge()
    println("hogehoge")
  }
}

class Class1 extends TraitA with TraitB
object Main extends App {
  val instance = new Class1

  instance.hoge()
  // ParentTrait: hoge
  // hoge
  // hogehoge
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/6a8a5cd6-feb9-4943-bea6-489a4fd62c0a.png)