I am trying to write some Scala code that needs to do something like:
class Test[Type] {
def main {
SomeFunc classOf[Type]
val testVal: Type = new Type()
}
}
and it's failing. I'm obviously not understanding something about Scala generic parameters. Clearly, the misunderstanding is that in C++, templates essentially function like string substitutions, so new Type() will work as long as the class being passed in has a default constructor. However, in Scala, types are different kinds of objects.
Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses. The method listOfDuplicates takes a type parameter A and value parameters x and length .
sealed trait s (or sealed abstract class es) are also known as coproducts. case object s and Int , Double , String (etc) are known as values.
In Scala, an abstract class is constructed using the abstract keyword. It contains both abstract and non-abstract methods and cannot support multiple inheritances. A class can extend only one abstract class.
Trait supports multiple inheritance. Abstract Class supports single inheritance only. Trait can be added to an object instance. Abstract class cannot be added to an object instance.
As you point out, C++ has templates. In short, C++ says "there is a Test for all types T such that Test compiles." That makes it easy to implicitly add constraints on T, but on the down side they're implicit and may be hard for a user of your class to understand without reading code.
Scala's parametric polymorphism (aka generics) work much more like ML, Haskell, Java, and C#. In Scala, when you write "class Test[T]" you are saying "for all T there exists a type Test[T]" without constraint. That's simpler to reason about formally, but it does mean that you have to be explicit about constraints. For instance, in Scala you can say "class Test[T <: Foo]" to say that T must be a subtype of Foo.
C# has a way to add a constraint to T regarding constructors, but unfortunately Scala does not.
There are a couple of ways to solve your problem in Scala. One is typesafe but a bt more verbose. The other is not typesafe.
The typesafe way looks like
class Test[T](implicit val factory : () => T) {
val testVal = factory
}
Then you can have a body of factories for types useful in your system
object Factories {
implicit def listfact[X]() = List[X]()
implicit def setfact[X]() = Set[X]()
// etc
}
import Factories._
val t = new Test[Set[String]]
If users of your library need their own factories then they can add their own equivalent of the Factories object. One advantage to this solution is that anything with a factory can be used, whether or not there's a no-arg constructor.
The not-so-typesafe way uses reflection and a feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
With this version you still write
class Foo
val t = new Test[Foo]
However, if there's no no-arg constructor available you get a runtime exception instead of a static type error
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With