Абстрактные типы / Типовые параметры в Scala

StackOverflow https://stackoverflow.com/questions/818996

  •  03-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь написать некоторый код Scala, который должен сделать что-то вроде:

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

и он терпит неудачу. Я явно не понимаю что-то о общих параметрах Scala. Ясно, что недоразумение заключается в том, что в C ++ шаблоны по существу работают как строковые подстановки, поэтому new Type () будет работать, пока передаваемый класс имеет конструктор по умолчанию. Однако в Scala типы - это разные типы объектов.

Это было полезно?

Решение

Как вы указали, в C ++ есть шаблоны. Короче говоря, в C ++ написано & Quot; для всех типов T существует Test, который Test компилирует. & Quot; Это облегчает неявное добавление ограничений на T, но, с другой стороны, они неявные и могут быть трудными для понимания пользователем вашего класса без чтения кода.

Параметрический полиморфизм Scala (он же дженерики) работает намного больше, чем ML, Haskell, Java и C #. В Scala, когда вы пишете & Quot; класс Test [T] & Quot; вы говорите " для всех T существует тип Test [T] " без ограничений. Проще рассуждать формально, но это означает, что вы должны быть откровенны в отношении ограничений. Например, в Scala вы можете сказать & Quot; class Test [T & Lt ;: Foo] & Quot; сказать, что T должен быть подтипом Foo.

C # имеет способ добавить ограничение к T в отношении конструкторов, но, к сожалению, Scala этого не делает.

Есть несколько способов решить вашу проблему в Scala. Один типобезопасен, но более многословен. Другой не безопасен.

Типобезопасный способ выглядит как

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]]

Если пользователям вашей библиотеки нужны собственные фабрики, они могут добавить свой собственный эквивалент объекта фабрики. Одним из преимуществ этого решения является то, что можно использовать все, что связано с фабрикой, независимо от того, существует ли конструктор без аргументов.

Не очень безопасный тип использует отражение и функцию в 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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top