2014-12-24 16 views
13

Proprio ieri ho deciso di iniziare ad apprendere il linguaggio di programmazione Haxe dopo aver utilizzato Actionscript 3 negli ultimi anni. Oggi ho esplorato i tipi astratti e mi sono reso conto che sembrano molto diversi dalle classi astratte in Java. Sto iniziando a cogliere un po 'di quello che fanno, ma non sono sicuro di quali abstracts siano usati. Cosa costituisce l'uso corretto degli abstract in Haxe e quando dovrei favorirli rispetto alle classi?Uso corretto degli abstract

Ad esempio, di seguito è una definizione incompleta per un tipo di numero complesso utilizzando un tipo astratto. Dovrei preferire questa o solo una classe ordinaria?

abstract Complex({real:Float, imag:Float}) { 
    public function new(real:Float, imag:Float) { 
     this = { real: real, imag: imag }; 
    } 

    public function real():Float { return this.real; } 
    public function imag():Float { return this.imag; } 

    @:op(A + B) 
    public static function add(lhs:Complex, rhs:Complex):Complex { 
     return new Complex(lhs.real() + rhs.real(), lhs.imag() + rhs.imag()); 
    } 

    public function toString():String { 
     return real() + " + " + imag() + "i"; 
    } 
} 

risposta

22

Infatti gli abstract non sono affatto come le classi astratte in Java. I tipi astratti in Haxe sono potenti e interessanti. La loro caratteristica principale è che sono tipi che esistono solo in fase di compilazione. Al momento dell'esecuzione vengono completamente sostituiti dal tipo avvolto. I metodi vengono trasformati in funzioni statiche. Nel caso in cui hai descritto tutte le tue istanze saranno sostituite da oggetti anonimi con i due campi real e imag. È un buon caso d'uso? Probabilmente sì dato che un tipo di Complesso non è pensato per essere esteso e probabilmente vorrai definire qualche sovraccarico dell'operatore (come hai fatto per l'aggiunta).

Per mantenerlo ancora più leggero, è possibile utilizzare un Array<Float> come tipo avvolto in cui il primo elemento è la parte reale e il secondo quello immaginario.

Quindi, cosa c'è di buono nei tipi astratti?

  • aggiungono i tipi semantici (in particolare i tipi primitivi). Ad esempio, è possibile definire uno abstract RGB(Int) {} per generare sempre una codifica del colore molto efficiente con il vantaggio di metodi e proprietà. Oppure potresti avere un abstract Path(String) {} per gestire convenientemente la concatenazione di percorsi, percorsi relativi e simili.
  • è possibile definire l'overloading dell'operatore. Nel caso precedente potresti fare qualcosa come white + black e ottenere qualcosa di significativo da esso.
  • in modo simile all'overloading dell'operatore, gli abstract possono definire i cast impliciti da e ad altri tipi. Nel caso di RGB sopra, si potrebbe facilmente definire un metodo fromString() per analizzare una stringa esadecimale in un Int che rappresenta un colore. Con il cast implicito che potevi fare: var color : RGB = "#669900";. thx.color definisce molti abstract per la gestione del colore.
  • sono ideali per avvolgere il potentissimo Enum di Haxe. Con un abstract è possibile aggiungere metodi e proprietà alle enumerazioni (che a livello nativo non supportano nessuna di queste).
  • sono ideali per avvolgere codice ottimizzato. I metodi astratti possono essere sottolineati e il tipo avvolto garantisce che non si aggiungano ulteriori livelli di riferimento indiretto durante l'esecuzione del codice.

Cosa non va bene? O meglio, cosa dovremmo sapere sugli abstract?

  • poiché sono solo un artefatto di compilazione, non è possibile utilizzare i controlli di runtime (ad esempio: no Std.is(value, MyAbstract)).
  • gli abstract non sono classi, quindi nessuna ereditarietà.
+0

Questo primo punto elenco ha fatto davvero clic per me! Ora capisco perché sono chiamati "abstract" (: –