Scala：コンストラクタに「上限境界」制約をかけて、条件を充足しないインスタンスの生成を禁じる


###次のスクリプトファイルをコンパイルして実行する

####（ クラス間の継承関係 ）

HighLoyalityCustomerクラス --> NormalCustomerクラス --> Customerクラス --> Personクラス

####（ 継承クラスのコンストラクタに付けた「上限境界」制約）

- CustomerContainerクラスのコンストラクタは、Customerクラスそれ自体か、Customerクラスのサブクラスのみ受け入れる。

>```Scala:
>class CustomerContainer[C <: Customer](c: C) {
```

- GoodCustomerContainerクラスのコンストラクタは、HighLoyalityCustomerクラスそれ自体か、HighLoyalityCustomerクラスのサブクラスのみ受け入れる。

>```Scala:
>class GoodCustomerContainer[H <: HighLoyalityCustomer](h: H) {
>```


```Scala:bounded_class_test.scala 
object BoundedClassTest {
  def main(args: Array[String]){
     val customerContainer = new CustomerContainer[NormalCustomer](new NormalCustomer("Johnny", 15000))
     val goodCustomerContainer = new GoodCustomerContainer[HighLoyalityCustomer](new HighLoyalityCustomer("Ben", 7200000, 235))
     println(customerContainer.customer_name)
     println(goodCustomerContainer.customer_name)
     }

abstract class Person() {
 def get_name: String
}

class Customer(val cust_name: String) extends Person() {
  override def get_name: String = cust_name
}

class NormalCustomer(cust_name: String, val total_sales_price: Int) extends Customer(cust_name) {
  def get_sales_volume: Int = total_sales_price
}

class HighLoyalityCustomer(cust_name: String, total_sales_price: Int, val total_purchased_num: Int) extends NormalCustomer(cust_name: String, total_sales_price: Int) {
  def get_sales_count: Int = total_purchased_num
}


class CustomerContainer[C <: Customer](c: C) {
  def customer_name: String = c.get_name
}

class GoodCustomerContainer[H <: HighLoyalityCustomer](h: H) {
  def customer_name: String = h.get_name
}
}
```


###コンパイル実行（scalacコマンド）

- 警告メッセージWarningは出ますが、コンパイルに成功しました。
- スクリプトファイル内に記述したインスタンス生成のコードは、最初に見たすべての「上限境界」制約を満たしているので、コンパイルエラーを起こしませんでした。

```bash:Terminal
electron@diynoMacBook-Pro scala_test % scalac bounded_class_test.scala
warning: 1 deprecation (since 2.13.0); re-run with -deprecation for details
1 warning
electron@diynoMacBook-Pro scala_test %
```

- 実行ファイル（*BoundedClassTest .class*）以下、各種ファイルが生成されている

```bash:Terminal
electron@diynoMacBook-Pro scala_test % ls | grep BoundedClassTest 
BoundedClassTest$.class
BoundedClassTest$Animal.class
BoundedClassTest$Cat.class
BoundedClassTest$Customer.class
BoundedClassTest$CustomerContainer.class
BoundedClassTest$Dog.class
BoundedClassTest$GoodCustomerContainer.class
BoundedClassTest$HighLoyalityCustomer.class
BoundedClassTest$Lion.class
BoundedClassTest$NormalCustomer.class
BoundedClassTest$Person.class
BoundedClassTest$Pet.class
BoundedClassTest$PetContainer.class
BoundedClassTest.class
electron@diynoMacBook-Pro scala_test % 
```

###scalaコマンドでクラスファイルを実行

```bash:Terminal
electron@diynoMacBook-Pro scala_test % scala BoundedClassTest
Johnny
Ben
electron@diynoMacBook-Pro scala_test % 
```

###REPLに読み込むスクリプトファイルを作成する

```Scala:bounded_class_test2.scala 
abstract class Person() {
 def get_name: String
}

class Customer(val cust_name: String) extends Person() {
  override def get_name: String = cust_name
}

class NormalCustomer(cust_name: String, val total_sales_price: Int) extends Customer(cust_name) {
  def get_sales_volume: Int = total_sales_price
}

class HighLoyalityCustomer(cust_name: String, total_sales_price: Int, val total_purchased_num: Int) extends NormalCustomer(cust_name: String, total_sales_price: Int) {
  def get_sales_count: Int = total_purchased_num
}


class CustomerContainer[C <: Customer](c: C) {
  def customer_name: String = c.get_name
}

class GoodCustomerContainer[H <: HighLoyalityCustomer](h: H) {
  def customer_name: String = h.get_name
}
}
```

- REPLにロードして実行する。
- REPL環境内で、最初に見たすべての「上限境界」制約を満たすインスタンス生成と、制約を満たさないインスタンス生成（の試み）を実行してみます。

```bash:Terminal
electron@diynoMacBook-Pro scala_test % scala
Welcome to Scala 2.13.6 (OpenJDK 64-Bit Server VM, Java 17.0.1).
Type in expressions for evaluation. Or try :help.

scala> :load bounded_class_test2.scala
val args: Array[String] = Array()
Loading bounded_class_test2.scala...
class Person
class Customer
class NormalCustomer
class HighLoyalityCustomer
class CustomerContainer
class GoodCustomerContainer

       }
       ^
bounded_class_test2.scala:1: error: eof expected but '}' found.

scala> 
```

- customerContainerクラスのコンストラクタは、NormalCustomerクラスのインスタンス（*new NormalCustomer("Johnny", 15000)*）を初期化変数として受け入れる。

```bash:Terminal
scala> val customerContainer = new CustomerContainer[NormalCustomer](new NormalCustomer("Johnny", 15000))
val customerContainer: CustomerContainer[NormalCustomer] = CustomerContainer@3cc2e3e

scala> println(customerContainer.customer_name)
Johnny

```

- goodCustomerContainerクラスのコンストラクタは、HighLoyalityCustomerクラスのインスタンス（*new HighLoyalityCustomer("Ben", 7200000, 235)）を初期化変数として受け入れる。

```bash:Terminal
scala> val goodCustomerContainer = new GoodCustomerContainer[HighLoyalityCustomer](new HighLoyalityCustomer("Ben", 7200000, 235))
val goodCustomerContainer: GoodCustomerContainer[HighLoyalityCustomer] = GoodCustomerContainer@7272914b

scala> println(goodCustomerContainer.customer_name)
Ben
```

- goodCustomerContainerクラスのコンストラクタは、NormalCustomerクラスのインスタンス（*new NormalCustomer("Johnny", 15000)*）を初期化変数として受け入れることができない。
- goodCustomerContainerクラスは、上限境界であるHighLoyalityCustomerそれ自体か、HighLoyalityCustomerのサブクラスしか、初期化変数として受け入れない。

```bash:Terminal
scala> val goodCustomerContainer = new GoodCustomerContainer[NormalCustomer](new NormalCustomer("Johnny", 15000))
           ^
       error: type arguments [NormalCustomer] do not conform to class GoodCustomerContainer's type parameter bounds [H <: HighLoyalityCustomer]
                                       ^
       error: type arguments [NormalCustomer] do not conform to class GoodCustomerContainer's type parameter bounds [H <: HighLoyalityCustomer]
```

- Personクラスは抽象クラスなので、インスタンスオブジェクトを生成できない（*new Person()*は実行不能）
- 抽象クラスを継承したサブクラスからは、インスタンスを生成できる（そのサブクラスが抽象クラスでない場合）

```bash:Terminal
scala> val goodCustomerContainer = new GoodCustomerContainer[Person](new Person())
                                                                     ^
       error: class Person is abstract; cannot be instantiated

```

- CustomerContainerクラスのコンストラクタは、HighLoyalityCustomerクラスのインスタンス（*new HighLoyalityCustomer("Ben", 7200000, 235)*）を初期化変数として受け入れる。
- CustomerContainerクラスのコンストラクタは、上限境界であるCustomerクラスそれ自体か、Customerクラスのサブクラスのインスタンスを受け入れることができる。
- HighLoyalityCustomerクラスは、この「Customerクラスのサブクラス」という制約条件を満たしている。

```bash:Terminal
scala> val customerContainer = new CustomerContainer[HighLoyalityCustomer](new HighLoyalityCustomer("Ben", 7200000, 235))
val customerContainer: CustomerContainer[HighLoyalityCustomer] = CustomerContainer@23adbd6b

scala> println(customerContainer.customer_name)
Ben

scala>
```

ここで、以下が成立します。

- HighLoyalityCustomerクラスは、NormalCustomerクラスのサブクラスである。
- そのため、__もしも、CustomerContainerクラスの下限境界がNormalCustomerであったとしたら、「NormalCustomerのサブクラスである」 HighLoyalityCustomerクラスは受け入れられなかった。
- HighLoyalityCustomerクラスは、「NormalCustomerクラスそれ自体か、NormalCustomerクラスのスーパークラスであること」という制約条件を満たさないため。

REPLを抜ける。

```bash:Terminal
scala> :q
electron@diynoMacBook-Pro scala_test % 
```

__（ 参考にしたウェブページ ）__

https://docs.scala-lang.org/ja/tour/upper-type-bounds.html

https://takafumi-s.hatenablog.com/entry/2015/08/20/135525

http://www.nct9.ne.jp/m_hiroi/java/scala09.html