Abstract Types / Type Parameters in Scala

前端 未结 1 1166
名媛妹妹
名媛妹妹 2020-12-05 03:40

I am trying to write some Scala code that needs to do something like:

class Test[Type] { 
   def main {
       SomeFunc classOf[Type]
       val testVal: Typ         


        
相关标签:
1条回答
  • 2020-12-05 03:57

    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)
    
    0 讨论(0)
提交回复
热议问题