272 votes

Scala : Abstract types génériques vs

Je lisais A Tour de Scala : Types abstraits. Quand est-il préférable d’utiliser des types abstraits ?

Par exemple,

plutôt que génériques, par exemple,

40voto

Daniel Yankowsky Points 3719

J’ai eu la même question quand je lisais Scala.

L’avantage de l’utilisation de génériques, c’est que vous créez une famille de types. Personne ne devez sous-classe tampon - ils peuvent seulement utiliser un tampon [tout], tampon [chaîne].

Si vous utilisez un type abstrait, alors les gens seront obligés de créer une sous-classe. Gens devront classes comme AnyBuffer, StringBuffer, etc.

Vous devez décider quel est le meilleur pour votre besoin particulier.

20voto

ayvango Points 1437

Vous pouvez utiliser les types abstract en conjonction avec les paramètres de type d'établir des modèles personnalisés.

Imaginons que vous avez besoin d'établir un modèle connecté avec trois traits:

trait AA[B,C]
trait BB[C,A]
trait CC[A,B]

dans la façon dont les arguments mentionnés dans les paramètres de type AA,BB,CC lui-même respectueusement

Vous pouvez venir avec un certain genre de code:

trait AA[B<:BB[C,AA[B,C]],C<:CC[AA[B,C],B]]
trait BB[C<:CC[A,BB[C,A]],A<:AA[BB[C,A],C]]
trait CC[A<:AA[B,CC[A,B]],B<:BB[CC[A,B],A]]

qui ne fonctionne pas de cette manière simple, en raison de paramètre de type d'obligations. Vous devez il covariante d'hériter correctement

trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]]
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]]
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]]

Cette un échantillon de la compilation, mais elle fixe des exigences fortes sur la variance des règles et ne peut pas être utilisé dans certaines occasions

trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]] {
  def forth(x:B):C
  def back(x:C):B
}
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]] {
  def forth(x:C):A
  def back(x:A):C
}
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]] {
  def forth(x:A):B
  def back(x:B):A
}

Le compilateur objet avec des tas de variance vérifier les erreurs

Dans ce cas, vous peut recueillir toutes les exigences de type supplémentaire de traits de caractère et de paramétrer d'autres traits sur elle

//one trait to rule them all
trait OO[O <: OO[O]] { this : O =>
  type A <: AA[O]
  type B <: BB[O]
  type C <: CC[O]
}
trait AA[O <: OO[O]] { this : O#A =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:B):C
  def right(r:C):B = r.left(this)
  def join(l:B, r:C):A
  def double(l:B, r:C):A = this.join( l.join(r,this), r.join(this,l) )
}
trait BB[O <: OO[O]] { this : O#B =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:C):A
  def right(r:A):C = r.left(this)
  def join(l:C, r:A):B
  def double(l:C, r:A):B = this.join( l.join(r,this), r.join(this,l) )
}
trait CC[O <: OO[O]] { this : O#C =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:A):B
  def right(r:B):A = r.left(this)
  def join(l:A, r:B):C
  def double(l:A, r:B):C = this.join( l.join(r,this), r.join(this,l) )
}

Maintenant, nous pouvons écrire en béton pour la représentation des modèles décrits, définir la gauche et rejoindre les méthodes de toutes les classes et obtenir le droit et le double pour libre

class ReprO extends OO[ReprO] {
  override type A = ReprA
  override type B = ReprB
  override type C = ReprC
}
case class ReprA(data : Int) extends AA[ReprO] {
  override def left(l:B):C = ReprC(data - l.data)
  override def join(l:B, r:C) = ReprA(l.data + r.data)
}
case class ReprB(data : Int) extends BB[ReprO] {
  override def left(l:C):A = ReprA(data - l.data)
  override def join(l:C, r:A):B = ReprB(l.data + r.data)
}
case class ReprC(data : Int) extends CC[ReprO] {
  override def left(l:A):B = ReprB(data - l.data)
  override def join(l:A, r:B):C = ReprC(l.data + r.data)
}

Donc, à la fois abstrait types de paramètres de type et sont utilisés pour créer des abstractions. Ils ont tous deux faibles et points forts. Résumé types sont plus spécifiques et capables de décrire tout type de structure, mais il est verbeux et nécessitent explicite spécifiée. Les paramètres de Type peut créer des tas de types instantanément, mais vous donne plus de s'inquiéter à propos de l'héritage et le type de limites.

Ils donnent synergie les uns des autres et peuvent être utilisés conjointement pour créer des complexes des abstractions qui ne peut pas être exprimée par un seul d'entre eux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X