目標
Scala において,(_ <: AnyVal)
のようなwildcard を用いた定義式がどううれしいかを論じる.
wildcard の簡単な説明
generic type を用いるとき,型安全性のため Covariant な型(A <: B のときのA)の操作に制限がある.例として,型安全のために, covariant type の compound type (List[A] や Set[A], Array[A])に別の covariant type を挿入できうる操作が定義できないことがある. (詳しくは使用例で)そこで,wild card を用いることにより,generic type に対しするメソッドを型安全となるよう柔軟に定義できる.
使用例とメリット
独自の Int クラスと Double クラス, List クラスを定義し,独自の List クラスに Int や Double を追加していくような実装を考える.ここで,独自の Int クラスと Double クラスはともに NumericVal クラスの covariant タイプである.各クラスの定義は次のようになる.
// 数値クラス
abstract class NumericVal (){}
class MyInt () extends NumericVal {val value = 0}
class MyDouble () extends NumericVal {val value = 0.0}
// リストクラス
class MyList[T](){
var list:List[T] = List()
def add(elem:T) {list = elem :: list}
}
この例では, generic type を用いて定義した独自のリストクラス MyList に MyInt, MyDouble を挿入するメソッドを, wildcard で型安全に定義できることを示す.
MyList クラスは, generic type T のリストを保持し, add メソッドを通じてリストに T のインスタンスを追加できる.
addElemToList メソッドでは,次のことを実現したいとする.
1. MyList[NumericVal] に MyInt や MyDouble のインスタンスを追加する.
2. MyList[MyInt] に MyInt のインスタンスを追加する.
3. MyList[MyDouble] に MyDouble のインスタンスを追加する.
しかし,これを型安全に定義するのは難しい.
もし次のように定義する場合,
def addElemToList(elem:NumericVal, list:MyList[NumericVal]){
list.add(elem)
}
SBT のコンパイラではMyList[MyInt] <: MyList[NumericVal]
が成り立たないため(後述),要件2と3を満たさない.仮にMyList[MyInt] <: MyList[NumericVal]
が成り立つとしても,次のように型安全でないメソッドが定義できうるため,型検査をパスしない.
val intList = new MyList[MyInt]()
val myDouble = new MyDouble()
addElemToList(myDouble, intList) // MyList[MyInt] に MyDouble が入っちゃう
そこで, wildcard を用いることにより,次のように型安全にメソッドを定義できる.
object Main extends App {
def addElemToList[A<:NumericVal](elem:A, list:MyList[_ >:A]){
list.add(elem)
}
// 要件1を満たす.
var numericList = new MyList[NumericVal]()
addElemToList(new MyInt(), numericList)
addElemToList(new MyDouble(), numericList)
// 要件2を満たす.
var intList = new MyList[MyInt]()
addElemToList(new MyInt(), intList)
// 要件3を満たす.
var doubleList = new MyList[MyDouble]()
addElemToList(new MyDouble(), doubleList)
}
前述のとおり,SBT のコンパイラではMyList[MyInt] <: MyList[NumericVal]
が成り立たない.これは次のエラーにより確認できる.
[error] /home/ytakahashi/project/classes/programingDesign/problem02/src/main/scala/example/Hello.scala:31:30: type mismatch;
[error] found : problem02.MyList[problem02.MyInt]
[error] required: problem02.MyList[problem02.NumericVal]
[error] Note: problem02.MyInt <: problem02.NumericVal, but class MyList is invariant in type T.
[error] You may wish to define T as +T instead. (SLS 4.5)
[error] addElemToList(new MyInt(), intList)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed Aug 8, 2018 11:30:35 PM