423 votes

Quelle est la différence entre la Scala des types et des sous-classes de caractère ?

Auto-types semblent être important, alors pourquoi sont-ils utiles?

De ce que j'ai pu rassembler, une auto-type pour un trait d'Un:

trait B
trait A { this: B => }

dit que "l'Un ne peut pas être mélangé dans une classe concrète qui n'a pas aussi de prolonger B".

(Note: je suis aussi de voir cela n'était pas dans le REPL lorsqu'il est mélangé dans une classe abstraite qui ne s'étend pas B, dont le livre "Programmation Scala", dit devrait fonctionner, mais je suis en utilisant un Scala 2.8 quotidien de construire.)

Mais si je dis:

trait B
trait A extends B

cela signifie que "tout (concrète ou abstraite) de la classe de mélanger dans Une même sera également le mélange dans B". Mais n'est-ce pas ces deux déclarations signifient la même chose? L'auto-type semble ne servir qu'à créer la possibilité d'une simple erreur de compilation.

J'ai considéré que la différence peut être de l'ordre de la surcharge de fonction lors de l'utilisation d'un auto-type, au lieu d'une s'étend de la clause... Mais cela semble être un mineur de distinction, pas digne de le battage médiatique sur l'auto-types. Ce qui me manque?

298voto

Daniel C. Sobral Points 159554

Il est utilisé pour l'Injection de Dépendance, comme dans le Modèle de Gâteau. Il existe un excellent article couvrant de nombreuses formes différentes de l'injection de dépendance dans Scala, y compris le Modèle de Gâteau. Si vous regardez en haut de Google pour le Modèle de Gâteau et de la Scala, vous aurez beaucoup de liens, y compris des présentations et des vidéos. Pour l'instant, voici un lien vers une autre question.

Maintenant, pour ce qui est la différence entre une auto de type et extension d'un trait, c'est simple. Si vous dites B extends A, alors B est un A. Lorsque vous faites l'injection de dépendance, vous souhaitez B pour exiger A, de ne pas être un A. Par exemple:

scala> trait User { def name: String }
defined trait User

scala> trait Tweeter {
     |   user: User =>
     |   def tweet(msg: String) = println(s"$name: $msg")
     | }
defined trait Tweeter

scala> trait Wrong extends Tweeter {
     |   def noCanDo = name
     | }
<console>:9: error: illegal inheritance;
 self-type Wrong does not conform to Tweeter's selftype Tweeter with User
       trait Wrong extends Tweeter {
                           ^
<console>:10: error: not found: value name
         def noCanDo = name
                       ^

Ce qui entraînerait pas d'erreur si le sous-classement ont été utilisés.

164voto

Mushtaq Ahmed Points 2518

Auto types vous permettent de définir des dépendances cycliques. Par exemple, vous pouvez atteindre cet objectif:

trait A {self: B=>}
trait B {self: A=>}

L'héritage de l'aide s'étend ne le permet pas. Essayez:

trait A extends B
trait B extends A
error:  illegal cyclic reference involving trait A

Dans le Odersky livre, regarder la section de 33,5 (Création de feuille de calcul de l'INTERFACE utilisateur chapitre), où il est dit:

Dans la feuille de calcul, exemple, Modèle de classe hérite de l'Évaluateur et ainsi, les gains de l'accès à sa méthode d'évaluation. Aller dans l'autre sens, la classe Évaluateur définit son auto type de Modèle, comme ceci:

package org.stairwaybook.scells
trait Evaluator { this: Model => ...

Espérons que cette aide.

62voto

Dave Griffith Points 12923

Une différence supplémentaire est que les types peut spécifier des types sans classe. Par exemple

Le type libre ici est un type de construction. L’effet est de dire que tout ce qui se mélange dans Foo doit implémenter une méthode de « fermer » de no-arg renvoyer l’appareil. Cela permet mixins sécuritaire d’une identification de canard.

13voto

lcn Points 379

La Section 2.3 "Selftype Annotations" de Martin Odersky d'origine de la Scala de papier Évolutive Composant Abstractions explique d'ailleurs le but de selftype au-delà de mixin composition très bien: prévoir une autre façon d'associer une classe avec un type abstrait.

L'exemple donné dans le document a été comme suit et il ne semble pas avoir une élégante sous-classe correspondant:

abstract class Graph {
  type Node <: BaseNode;
  class BaseNode {
    self: Node =>
    def connectWith(n: Node): Edge =
      new Edge(self, n);
  }
  class Edge(from: Node, to: Node) {
    def source() = from;
    def target() = to;
  }
}

class LabeledGraph extends Graph {
  class Node(label: String) extends BaseNode {
    def getLabel: String = label;
    def self: Node = this;
  }
}

10voto

Rich Oliver Points 1942

Commençons avec le cyclique de la dépendance.

trait A {
  selfA: B =>
  def fa: Int }

trait B {
  selfB: A =>
  def fb: String }

Toutefois, la modularité de cette solution n'est pas aussi grand qu'il peut sembler à première vue, parce que vous pouvez remplacer l'autonomie des types comme ça:

trait A1 extends A {
  selfA1: B =>
  override def fb = "B's String" }
trait B1 extends B {
  selfB1: A =>
  override def fa = "A's String" }
val myObj = new A1 with B1

Bien que, si vous remplacer un membre d'une auto de type, vous perdez l'accès au membre d'origine, qui peut encore être accessibles par le biais de super en utilisant l'héritage. Donc ce qui est vraiment acquise au cours de l'utilisation de l'héritage est:

trait AB {
  def fa: String
  def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }        
trait B1 extends AB
{ override def fb = "B's String" }    
val myObj = new A1 with B1

Maintenant, je ne peux pas prétendre le comprendre toutes les subtilités du modèle de gâteau, mais il me semble que la principale méthode de l'application de la modularité est au travers de la composition plutôt que de l'héritage ou de l'auto types.

L'héritage de la version est plus courte, mais la principale raison, je préfère l'héritage sur l'auto types est que je trouve qu'il est beaucoup plus difficile à obtenir l'initialisation de l'ordre correct avec auto types. Cependant, il ya certaines choses que vous pouvez faire avec l'auto types que vous ne pouvez pas faire avec l'héritage. Auto types peuvent utiliser un type pendant que l'héritage exige un trait ou d'une classe, comme dans:

trait Outer
{ type T1 }     
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.

Vous pouvez même le faire:

trait TypeBuster
{ this: Int with String => }

Bien que vous ne serez jamais en mesure de l'instancier. Je ne vois pas de raison absolue pour ne pas être en mesure d'hériter d'un type, mais je pense qu'il serait utile de disposer de chemin constructeur des classes et des traits que nous avons constructeur de type traits / classes. Comme malheureusement

trait InnerA extends Outer#Inner //Doesn't compile

Nous avons ceci:

trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }

Ou ceci:

  trait Outer
  { trait Inner }     
  trait InnerA
  {this: Outer#Inner =>}
  trait InnerB
  {this: Outer#Inner =>}
  trait OuterFinal extends Outer
  { val myVal = new InnerA with InnerB with Inner }

Un point qui devrait être empathised plus, c'est que les traits peuvent s'étend classes. Merci à David Maclver pour le rappeler. Voici un exemple de mon code:

class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }    
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]

ScnBase hérite de la Balançoire Cadre de la classe, de sorte qu'il pourrait être utilisé comme une auto de type et ensuite mélangée à la fin (à l'instanciation). Toutefois, val geomR doit être initialisée avant d'être utilisée par héritant des traits. Nous avons donc besoin d'une classe à appliquer avant l'initialisation de geomR. La classe ScnVista peut alors être hérité de par de multiples orthogonale de traits qui peuvent eux-mêmes être héritée de. À l'aide de plusieurs paramètres de type (génériques) propose une autre forme de modularité.

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