2009-05-04 5 views
16

Je suis en train d'écrire un code Scala qui a besoin de faire quelque chose comme:Résumé Paramètres Types/types Scala

class Test[Type] { 
    def main { 
     SomeFunc classOf[Type] 
     val testVal: Type = new Type() 
    } 
} 

et il est défaillant. Je ne comprends évidemment pas quelque chose sur les paramètres génériques de Scala. Clairement, le malentendu est qu'en C++, les templates fonctionnent essentiellement comme des substitutions de chaînes, donc le nouveau type() fonctionnera tant que la classe transmise aura un constructeur par défaut. Cependant, dans Scala, les types sont différents types d'objets.

+0

Qu'est-ce qu'une question? – stepancheg

+0

Comment est-ce que je fais ce travail? – bsdfish

Répondre

28

Comme vous le signalez, C++ a des modèles. En bref, C++ dit "il y a un test pour tous les types T tels que Test compile". Cela facilite l'ajout implicite de contraintes sur T, mais elles sont implicites et peuvent être difficiles à comprendre pour un utilisateur de votre classe sans lire le code.

Le polymorphisme paramétrique de Scala (aussi connu sous le nom de génériques) fonctionne beaucoup plus comme ML, Haskell, Java et C#. Dans Scala, quand vous écrivez "class Test [T]" vous dites "pour tout T il existe un type Test [T]" sans contrainte. C'est plus simple à raisonner formellement, mais cela signifie que vous devez être explicite sur les contraintes. Par exemple, dans Scala, vous pouvez dire "test de classe [T <: Foo]" pour dire que T doit être un sous-type de Foo. C# a un moyen d'ajouter une contrainte à T concernant les constructeurs, mais malheureusement Scala ne le fait pas.

Il existe plusieurs façons de résoudre votre problème dans Scala. L'un est typeafe mais un bt plus verbeux. L'autre n'est pas sûr.

La façon dont typesafe ressemble

class Test[T](implicit val factory :() => T) { 
    val testVal = factory 
} 

Ensuite, vous pouvez avoir un corps d'usines pour les types utiles dans votre système

object Factories { 
    implicit def listfact[X]() = List[X]() 
    implicit def setfact[X]() = Set[X]() 
    // etc 
} 

import Factories._ 
val t = new Test[Set[String]] 

Si les utilisateurs de votre bibliothèque ont besoin de leurs propres usines alors ils peuvent ajouter leur propre équivalent de l'objet Factories. Un avantage de cette solution est que n'importe quoi avec une usine peut être utilisé, qu'il y ait ou non un constructeur sans arg.

La façon pas si typesafe utilise la réflexion et une fonctionnalité de Scala appelé manifeste qui sont un moyen de contourner une contrainte Java en ce qui concerne l'effacement de type

class Test[T](implicit m : Manifest[T]) { 
    val testVal = m.erasure.newInstance().asInstanceOf[T] 
} 

Avec cette version, vous encore écrire

class Foo 
val t = new Test[Foo] 

Cependant, s'il n'y a pas à votre disposition constructeur sans arg obtenir une exception d'exécution au lieu d'une erreur de type statique

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set 
at java.lang.Class.newInstance0(Class.java:340) 
+0

Est-ce que l'une ou l'autre de ces approches fonctionne avec des constructeurs basés sur des paramètres pour T? –