Абстрактные типы / Типовые параметры в Scala - PullRequest
16 голосов
/ 04 мая 2009

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

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

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

1 Ответ

28 голосов
/ 04 мая 2009

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

Параметрический полиморфизм Scala (он же generics) работает намного больше, чем ML, Haskell, Java и C #. В Scala, когда вы пишете «класс Test [T]», вы говорите «для всех T существует тип Test [T]» без ограничений. Проще рассуждать формально, но это означает, что вы должны быть откровенны в отношении ограничений. Например, в Scala вы можете сказать «класс Test [T <: Foo]», чтобы сказать, что T должен быть подтипом Foo. </p>

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...