ご指摘のとおり、C++ にはテンプレートがあります。要するに、C++ は「Test がコンパイルするようなすべての型 T の Test がある」と言います。これにより、暗黙的に T に制約を簡単に追加できますが、マイナス面として、それらは暗黙的であり、クラスのユーザーがコードを読まないと理解するのが難しい場合があります。
Scala のパラメトリック ポリモーフィズム (別名ジェネリック) は、ML、Haskell、Java、および C# のように機能します。Scala では、「クラス Test[T]」と書くと、「すべての T に対して型 Test[T] が存在する」と制約なしで言っていることになります。これは形式的に推論する方が簡単ですが、制約について明示する必要があることを意味します。たとえば、Scala では、"class Test[T <: Foo]" と言って、T が Foo のサブタイプでなければならないことを示すことができます。
C# には、コンストラクタに関する制約を T に追加する方法がありますが、残念ながら Scala にはありません。
Scala で問題を解決するには、いくつかの方法があります。1 つはタイプセーフですが、もう少し冗長です。もう 1 つはタイプセーフではありません。
タイプセーフな方法は次のようになります
class Test[T](implicit val factory : () => T) {
val testVal = factory
}
次に、システムで役立つタイプのファクトリの本体を持つことができます
object Factories {
implicit def listfact[X]() = List[X]()
implicit def setfact[X]() = Set[X]()
// etc
}
import Factories._
val t = new Test[Set[String]]
ライブラリのユーザーが独自のファクトリを必要とする場合、ファクトリ オブジェクトに相当する独自のものを追加できます。このソリューションの利点の 1 つは、引数のないコンストラクターがあるかどうかに関係なく、ファクトリを持つものは何でも使用できることです。
タイプセーフではない方法では、リフレクションとマニフェストと呼ばれる Scala の機能を使用します。これは、型消去に関する Java の制約を回避する方法です。
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
このバージョンでは、まだ書き込みます
class Foo
val t = new Test[Foo]
ただし、使用可能な引数なしのコンストラクターがない場合は、静的型エラーの代わりに実行時例外が発生します。
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)