Абстрактные типы по сравнению с параметрами типа

В каких ситуациях абстрактные типы должны быть предпочтены по параметрам типа?

21
задан Jay Sinha 3 July 2010 в 08:24
поделиться

1 ответ

Чтобы добавить к моему предыдущему ответу на Abstract Тип по сравнению с параметрами , у вас также есть недавняя запись в блоге Джесси Эйхара (3 мая 2010 г.), в которой освещены некоторые ключевые различия:

trait C1[A] {
  def get : A
  def doit(a:A):A
}
trait C2 {
  type A
  def get : A
  def doit(a:A):A
}

В случае C2 , параметр «похоронен» (как внутренний абстрактный тип).
(за исключением того, что, как говорит ретроним, он на самом деле не похоронен, см. ниже)

В то время как для универсального типа параметр явно упоминается, помогая другим выражениям узнать, какой тип они должны использовать


Итак (C1: параметр):

//compiles
def p(c:C1[Int]) = c.doit(c.get)

Он компилируется, но вы явно указываете тип « A », который хотите использовать.

И (C2: абстрактный тип):

// doesn't compile
def p2(c:C2) = c.doit(c.get)
<console>:6: error: illegal dependent method type
       def p2(c:C2) = c.doit(c.get)
              ^

Он не компилируется, потому что ' A ' никогда не упоминается в определении p2, поэтому doit не знает при компиляции типа то, что предполагается вернуть.


При использовании абстрактных типов и , желающих избежать «утечки типа» в интерфейс (т. Е.желая раскрыть то, что на самом деле представляет собой ' A '), вы можете указать очень общий тип в качестве возврата для p2:

// compiles because the internals of C2 does not leak out
def p(c:C2):Unit = c.doit(c.get)

Или вы можете «исправить» этот тип непосредственно в doit ] функция:
def doit (a: A): Int вместо def doit (a: A): A , что означает:
def p2 (c: C2) = c.doit (c.get) будет компилироваться (даже если p2 не упоминает какой-либо возвращаемый тип)


Наконец ( retronym комментарий) вы можете указать A либо явно путем уточнения абстрактного параметра C2:

scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get)
p2: (c: C2{type A = Int})Int

Или путем добавления параметра типа (и уточнения с его помощью абстрактного типа C2!)

scala> def p2[X](c:C2 { type A = X }): X = c.doit(c.get)
p2: [X](c: C2{type A = X})X

Поэтому рекомендуется использовать абстрактные:

  • Если вы хотите скрыть точное определение член типа из клиентского кода , используйте абстрактный тип, как в C2 (но будьте осторожны с определением функции с помощью C2 )
  • Если вы хотите ковариантно переопределить тип в подклассах C2 используйте абстрактный тип (с абстракцией ограниченного типа)
  • W Если вы хотите смешать определения этих типов C2 с помощью признаков , используйте абстрактный тип (у вас не будет ' A », чтобы иметь дело при смешивании C2 с вашим классом: вы смешиваете только C2 )

Для остальных, где требуется создание экземпляра простого типа , используйте Параметры.
(если вы знаете, что расширение не потребуется, но вам все равно придется обрабатывать несколько типов: это то, для чего нужны типы параметров)


ретроним добавляет:

Основные отличия заключаются в

  • дисперсия : C2 может быть инвариантным только в A ,
  • способом, которым члены типа могут быть выборочно переопределены в подтипе (тогда как параметры типа необходимо повторно объявить и передать супертипу)

(как , иллюстрирующее здесь :

trait T1 {
  type t
  val v: t
}
trait T2 extends T1 {
  type t <: SomeType1
}
trait T3 extends T2 {
  type t <: SomeType2  // where SomeType2 <: SomeType1
}
class C extends T3 {
  type t = Concrete    // where Concrete <: SomeType2
  val v = new Concrete(...)
}

)

19
ответ дан 29 November 2019 в 21:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: