Domanda

In Scala, ho visto i costrutti

trait T extends S

e

trait T { this: S =>

utilizzato per realizzare le cose simili (cioè che i metodi astratti nella S devono essere definite prima un'istanza venga creata). Qual è la differenza tra loro? Perché utilizzare uno sopra l'altro?

È stato utile?

Soluzione

mi piacerebbe utilizzare auto-tipi di dipendenza-gestione: Questo tratto richiede un altro tratto da miscelare in E mi piacerebbe usare l'ereditarietà per perfezionare un altro tratto o interfaccia

..

A titolo di esempio:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

Ora, se FooRemoting e FooPersistence entrambi avrebbero ereditato da FooService, e FooService ha membri e dei metodi, come avrebbero Servizi simile?

Mentre per eredità, avremmo qualcosa come:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}

Altri suggerimenti

di tipo self annotazioni consentono di esprimere le dipendenze cicliche. Per esempio:

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

Questo non è possibile con una semplice eredità.

Dal momento che porre la domanda mi sono imbattuto in questi posti:

Spiros Tzavellas parla usando un tratto come l'interfaccia pubblica e il tipo di auto come aiutante che deve essere miscelato dalla classe di implementazione.

  

In conclusione, se vogliamo andare   Metodo di implementazioni all'interno tratti   allora rischiamo di inquinare l'interfaccia   di quei tratti con metodi astratti   che sostengono l'attuazione del   metodi concreti e non sono correlati   con la responsabilità principale della   tratto. Una soluzione a questo problema è   per spostare quei metodi astratti in   altri tratti e comporre i tratti   insieme usando tipo di auto annotazioni   e l'ereditarietà multipla.

Ad esempio:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

Una panoramica di Scala discute utilizzando il tipo di auto annotazioni con i membri di tipo astratti - presumibilmente E ' non è possibile extend un membro tipo astratto (?)

La risposta è "circolarità". Ma non solo.

Tipo di auto di annotazione risolve per me il problema fondamentale di eredità: cosa si eredita dalla non possono usare quello che sei. Con il tipo di auto, tutto diventa facile.

Il mio modello è il seguente e può essere considerato come una torta degenerata:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

È possibile esplodere la classe in più comportamenti che possono essere richiamati da qualsiasi parte del montaggio, durante il soggiorno in modo pulito digitato. Non c'è bisogno per il riferimento indiretto dolorosa troppo spesso (ea torto) identificato con il modello di torta.

La metà (se non la totalità) dei contorti framework Java DI degli ultimi dieci anni sono stati dedicati a fare questo, naturalmente senza la digitazione. La gente ancora utilizzando Java in questo campo sono chiaramente perdendo il loro tempo:. "SCALA ouakbar"

So che questa domanda è vecchio, ma vorrei aggiungere alcune precisazioni ed esempi.

Ci sono tre principali differenze tra i tipi di eredità e auto tratto.

Semantica

ereditarietà è uno dei rapporti con la maggior accoppiamento del paradigma dell'oggetto, se A estende B, ciò significa che A è un B.

Diciamo che abbiamo il seguente codice,

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

Stiamo dicendo che un cane è un animale. Siamo in grado di inviare i messaggi bark e stop a goodboy perché è un cane, è capire entrambi i metodi.

Ora supponiamo di avere un nuovo tratto,

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

Questa volta la sicurezza non è un animale, e questo va bene perché sarebbe semanticamente corretto se affermiamo che un Security è un animale, sono concetti diversi, che possono essere utilizzati insieme.

Quindi, ora siamo in grado di creare un nuovo tipo di cane,

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog è un cane, un animale e di sicurezza. E capire stop, bark e lookout perché è un cane con la sicurezza.

Ma cosa succede se creiamo un nuovo cane come questo?

val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 è solo un cane, quindi non possiamo chiamare il metodo lookout. (Okok, è un cane con sicurezza, ma abbiamo appena vedere un cane)

Cyclic dipendenze

Tipi Appartamenti ci permettono di creare dipendenze cicliche tra i tipi.

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

Il seguente codice non viene compilato.

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

Questo tipo di codice è molto comune a iniezione di dipendenza (pattern cake).

Versatilità

Ultimo ma non meno importante, che usa i nostri tratti può decidere l'ordine in cui vengono utilizzati, quindi grazie a Trait linearizzazione il risultato finale può essere diverso, anche se i tratti utilizzati sono gli stessi.

Con l'eredità normale non possiamo farlo, i rapporti tra i tratti e le classi sono fissi.

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

Spero che questo può essere utile.

Anche se non risponde alla tua domanda, stavo cercando di capire le annotazioni self-type e fondamentalmente sono perso nelle risposte, e in qualche modo ha finito per bicicletta attraverso le variazioni della sua domanda, che si concentra su l'uso di auto-annotazioni di tipo da affermando dipendenze.

Così qui ho posto una descrizione di un caso d'uso in cui auto-annotazioni di tipo sono ben illustrato, vale a dire qualcosa di simile a un caso di type-safe 'questo' come un sottotipo:

http://programming-scala.labs.oreilly.com/ch13. html # SelfTypeAnnotationsAndAbstractTypeMembers

sperando che sarebbe utile a coloro che finiscono su questa questione per caso (e, come me, non ha avuto tempo di leggere un libro scala prima di iniziare ad esplorare :-))

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top