Arv

Arv är en form av kodåteranvändning som gör att programmerare kan utveckla nya klasser som baseras på befintliga klasser. De befintliga klasserna kallas ofta basklasser eller superklasser, och de nya klasserna kallas underklasser. Den största fördelen med arv är att du kan återanvända kod från en basklass och ändå låta den befintliga koden vara oförändrad. Dessutom kräver arv inga ändringar i det sätt som andra klasser samverkar med basklassen. I stället för att du ändrar en befintlig klass som har testats noggrant eller som redan används, kan du med arv hantera denna klass som en integrerad modul som du kan utöka med ytterligare egenskaper och metoder. Därför kan du använda nyckelordet extends för att ange att en klass ska ärva från en annan klass.

Tack vare arv kan du också dra fördel av polymorfism i koden. Polymorfism är möjligheten att använda ett enstaka metodnamn för en metod som beter sig på olika sätt när den tillämpas på olika datatyper. Ett enkelt exempel är en basklass som heter Shape som har två underklasser som heter Circle och Square. Med klassen Shape definieras metoden area() som returnerar formens område. Om du implementerat polymorfism kan du anropa metoden area() på objekt av typen Circle och Square och få korrekta beräkningar. Tack vare arv möjliggörs polymorfism genom att underklasser kan ärva och omdefiniera, eller åsidosätta, metoder från basklassen. I följande exempel omdefinieras metoden area() med klasserna Circle och Square:

class Shape 
{ 
    public function area():Number 
    { 
        return NaN; 
    } 
} 
 
class Circle extends Shape 
{ 
    private var radius:Number = 1; 
    override public function area():Number 
    { 
        return (Math.PI * (radius * radius)); 
    } 
} 
 
class Square extends Shape 
{ 
    private var side:Number = 1; 
    override public function area():Number 
    { 
        return (side * side); 
    } 
} 
 
var cir:Circle = new Circle(); 
trace(cir.area()); // output: 3.141592653589793 
var sq:Square = new Square(); 
trace(sq.area()); // output: 1

Eftersom varje klass definierar en datatyp skapas med arv en speciell relation mellan en basklass och en klass som utökar den. En underklass garanteras äga alla basklassens egenskaper, vilket betyder att en instans av en underklass alltid kan ersättas med en instans av basklassen. Om en metod definierar en parameter av typen Shape, är det tillåtet att skicka ett argument av typen Circle eftersom Circle utökar Shape:

function draw(shapeToDraw:Shape) {} 
 
var myCircle:Circle = new Circle(); 
draw(myCircle);

Instansegenskaper och arv

En instansegenskap, som definierats med nyckelordet function, var eller const, ärvs av alla underklasser såvida inte egenskapen deklarerats med attributet private i basklassen. Klassen Event i ActionScript 3.0 har till exempel ett antal underklasser som ärver de egenskaper som är gemensamma för alla händelseobjekt.

För vissa typer av händelser innehåller klassen Event alla egenskaper som är nödvändiga för att definiera händelsen. Dessa typer av händelser kräver inte några fler instansegenskaper än de som definierats i klassen Event. Ett exempel på sådana händelser kan vara complete som inträffar när data har lästs in samt connect som inträffar när en nätverksanslutning har upprättats.

Följande exempel är ett utdrag från klassen Event som visar några av de egenskaper och metoder som ärvs av underklasser. Eftersom egenskaperna ärvs kan en instans av valfri underklass komma åt dessa egenskaper.

public class Event 
{ 
    public function get type():String; 
    public function get bubbles():Boolean; 
    ... 
 
    public function stopPropagation():void {} 
    public function stopImmediatePropagation():void {} 
    public function preventDefault():void {} 
    public function isDefaultPrevented():Boolean {} 
    ... 
}

Andra typer av händelser kräver unika egenskaper som inte är tillgängliga i klassen Event. Dessa händelser definieras med hjälp av underklassen till klassen Event så att nya egenskaper kan läggas till i de egenskaper som definierats i klassen Event. Ett exempel på en sådan underklass är klassen MouseEvent som lägger till egenskaper som är unika för händelser som kopplats till musrörelser och musklickningar, till exempel händelserna mouseMove och click. Följande exempel är ett utdrag från klassen MouseEvent som visar definitionen av egenskaper som finns i underklassen men inte i basklassen:

public class MouseEvent extends Event 
{ 
    public static const CLICK:String= "click"; 
    public static const MOUSE_MOVE:String = "mouseMove"; 
    ... 
 
    public function get stageX():Number {} 
    public function get stageY():Number {} 
    ... 
}

Åtkomstkontrollsspecifikationer och arv

Om en egenskap har deklarerats med nyckelordet public visas egenskapen för all kod. Detta betyder att nyckelordet public, i motsats till nyckelorden private, protected och internal, inte har några begränsningar för egenskapsarv.

Om en egenskap har deklarerats med nyckelordet private visas den bara i den klass som definierar den, vilket betyder att den inte ärvs av någon underklass. Detta skiljer sig från tidigare versioner av ActionScript, där nyckelordet private beter sig snarlikt nyckelordet protected i ActionScript 3.0.

Med nyckelordet protected anges att en egenskap visas i den klass som definierar den men också i alla underklasser. Med nyckelordet protected i ActionScript 3.0 visas en egenskap inte för alla andra klasser i samma paket, vilket sker med nyckelordet protected i programmeringsspråket Java. I ActionScript 3.0 kan bara underklasser komma åt en egenskap som deklarerats med nyckelordet protected. Dessutom visas en skyddad egenskap för en underklass vare sig underklassen är i samma paket som basklassen eller i ett annat paket.

Om du vill begränsa visningen av en egenskap i det paket som egenskapen är definierad i, använder du nyckelordet internal eller också använder du inte någon åtkomstkontrollsspecifikation. Åtkomstkontrollsspecifikationen internal är den standardspecifikation som tillämpas när ingen annan har angetts. En egenskap som markerats som internal ärvs bara av en underklass som finns i samma paket.

Du kan använda följande exempel för att visa hur varje åtkomstkontrollsspecifikation påverkar arv över paketgränserna. I följande kod definieras en huvudprogramklass som heter AccessControl och två andra klasser som heter Base och Extender. Klassen Base finns i paketet foo och klassen Extender, som är en underklass till Base, finns i paketet bar. Klassen AccessControl importerar bara klassen Extender och skapar en instans av Extender som försöker komma åt variabeln str som definierats i klassen Base. Variabeln str deklareras som public så att koden kompileras och körs, vilket visas i följande utdrag:

// Base.as in a folder named foo 
package foo 
{ 
    public class Base 
    { 
        public var str:String = "hello"; // change public on this line 
    } 
} 
 
// Extender.as in a folder named bar 
package bar 
{ 
    import foo.Base; 
    public class Extender extends Base 
    { 
        public function getString():String { 
            return str; 
        } 
    } 
} 
 
// main application class in file named AccessControl.as 
package 
{ 
    import flash.display.MovieClip; 
    import bar.Extender; 
    public class AccessControl extends MovieClip 
    { 
        public function AccessControl() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.str);// error if str is not public 
            trace(myExt.getString()); // error if str is private or internal 
        } 
    } 
}

Om du vill visa hur de andra åtkomstkontrollsspecifikationerna påverkar kompilering och körning av föregående exempel ändrar du str-variabelns åtkomstkontrollsspecifikation till private, protected eller internal när du har tagit bort eller kommenterat ut följande rad från klassen AccessControl:

trace(myExt.str);// error if str is not public

Du kan inte åsidosätta variabler

Egenskaper som har deklarerats med nyckelorden var eller const ärvs, men kan inte åsidosättas. Att åsidosätta en egenskap är att definiera om egenskapen i en underklass. Den enda typ av egenskap som kan åsidosättas är get- och set-åtkomstfunktioner (egenskaper som deklarerats med nyckelordet function). Även om du inte kan åsidosätta en instansvariabel kan du uppnå ett liknande resultat genom att skapa get- och set-metoder för instansvariabeln och åsidosätta metoderna.

Åsidosätta metoder

Att åsidosätta en metod är att omdefiniera beteendet för en ärvd metod. Statiska metoder ärvs inte och kan inte åsidosättas. Instansmetoder ärvs emellertid av underklasser och kan åsidosättas om följande två villkor uppfylls:

  • Instansmetoden har inte deklarerats med nyckelordet final i basklassen. När nyckelordet final används med en instansmetod anges programmerarens avsikt att förhindra att underklasser åsidosätter metoden.

  • Instansmetoden har inte deklarerats med åtkomstkontrollsspecifikationen private i basklassen. Om en metod har markerats som private i basklassen behöver du inte använda nyckelordet override när du definierar en metod med samma namn i underklassen, eftersom basklassens metod inte är synlig för underklassen.

    Om du vill åsidosätta en instansmetod som uppfyller dessa villkor, måste metoddefinitionen i underklassen använda nyckelordet override och dessutom matcha superklassversionen av metoden på följande sätt:

  • Åsidosättningsmetoden måste ha samma åtkomstkontrollnivå som basklassmetoden. Metoder som markerats som interna har samma åtkomstkontrollnivå som metoder som inte har någon åtkomstkontrollsspecifikation.

  • Åsidosättningsmetoden måste ha samma antal parametrar som basklassmetoden.

  • Åsidosättningsparametrarna måste ha samma datatypsanteckningar som parametrarna i basklassmetoden.

  • Åsidosättningsmetoden måste ha samma returtyp som basklassmetoden.

Namnen på parametrarna i åsidosättningsmetoden behöver däremot inte matcha namnen på parametrarna i basklassen, så länge som antalet parametrar och datatypen för respektive parameter matchar varandra.

Programsatsen super

När en programmerare åsidosätter en metod vill han ofta lägga till beteendet hos superklassmetoden som åsidosätts i stället för att ersätta beteendet helt och hållet. Detta kräver en funktion som tillåter att en metod i en underklass kan anropa sin egen superklassversion. Programsatsen super har en sådan funktion eftersom den innehåller en referens till den omedelbara superklassen. I följande exempel definieras klassen Base som innehåller metoden thanks() och underklassen Extender för klassen Base som åsidosätter metoden thanks(). Metoden Extender.thanks() använder programsatsen super för att anropa Base.thanks().

package { 
    import flash.display.MovieClip; 
    public class SuperExample extends MovieClip 
    { 
        public function SuperExample() 
        { 
            var myExt:Extender = new Extender() 
            trace(myExt.thanks()); // output: Mahalo nui loa 
        } 
    } 
} 
 
class Base { 
    public function thanks():String 
    { 
        return "Mahalo"; 
    } 
} 
 
class Extender extends Base 
{ 
    override public function thanks():String 
    { 
        return super.thanks() + " nui loa"; 
    } 
}

Åsidosätta get- och set-metoder

Även om du inte kan åsidosätta variabler som definierats i en superklass kan du åsidosätta get- och set-metoder. I följande kod åsidosätts en get-metod som heter currentLabel som definierats i klassen MovieClip i ActionScript 3.0:

package 
{ 
    import flash.display.MovieClip; 
    public class OverrideExample extends MovieClip 
    { 
        public function OverrideExample() 
        { 
            trace(currentLabel) 
        } 
        override public function get currentLabel():String 
        { 
            var str:String = "Override: "; 
            str += super.currentLabel; 
            return str; 
        } 
    } 
}

Utdata för programsatsen trace() i klasskonstruktorn OverrideExample är Override: null, vilket visar att exemplet kunde åsidosätta den ärvda egenskapen currentLabel.

Statiska egenskaper ärvs inte

Statiska egenskaper ärvs inte av underklasser. Det betyder att statiska egenskaper inte är tillgängliga via en instans av en underklass. En statisk egenskap är bara tillgänglig via klassobjektet som den definierats på. I följande kod definieras basklassen Base och underklassen Extender som utökar Base. Den statiska variabeln test definieras i klassen Base. Koden som skrivits i följande utdrag kompileras inte i strikt läge och genererar ett körningsfel i standardläge.

package { 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.test);// error 
        } 
    } 
} 
 
class Base { 
    public static var test:String = "static"; 
} 
 
class Extender extends Base { }

Enda sättet att komma åt den statiska variabeln test är via objektet class, vilket visas i följande kod:

Base.test;

Du kan emellertid definiera en instansegenskap med samma namn som en statisk egenskap. En sådan instansegenskap kan definieras i samma klass som den statiska egenskapen eller i en underklass. Klassen Base i föregående exempel kan alltså ha en instansegenskap som heter test. Följande kod kompileras och körs eftersom instansegenskapen ärvs av klassen Extender. Koden kompileras och körs också om definitionen av instansvariabeln test flyttas, men inte kopieras, till klassen Extender.

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.test);// output: instance 
        } 
    } 
} 
 
class Base 
{ 
    public static var test:String = "static"; 
    public var test:String = "instance"; 
} 
 
class Extender extends Base {}

Statiska egenskaper och omfångskedjan

Även om statiska egenskaper inte ärvs finns de i omfångskedjan för den klass som definierar dem och i alla underklasser till denna klass. Statiska egenskaper sägs vara i omfånget för de två klasser som de definierats i och i alla underklasser. Detta betyder att en statisk egenskap är direkt tillgänglig i brödtexten för den klass som definierar den statiska egenskapen och i alla underklasser till denna klass.

I följande exempel ändras de klasser som definierats i föregående exempel så att du kan se att den statiska variabeln test som definierats i klassen Base finns i omfånget till klassen Extender. Klassen Extender kan m.a.o. komma åt den statiska variabeln test utan att använda namnet på den klass som definierar test som prefix.

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
        } 
    } 
} 
 
class Base { 
    public static var test:String = "static"; 
} 
 
class Extender extends Base 
{ 
    public function Extender() 
    { 
        trace(test); // output: static 
    } 
     
}

Om du definierar en instansegenskap och ger den samma namn som en statisk egenskap i samma klass eller en superklass, får instansegenskapen högre prioritet i omfångskedjan. Instansegenskapen skuggar den statiska egenskapen, vilket betyder att värdet för instansegenskapen används i stället för värdet för den statiska egenskapen. I följande kod visas, att om klassen Extender definierar en instansvariabel som heter test, använder programsatsen trace() värdet för instansvariabeln i stället för värdet för den statiska variabeln:

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
        } 
    } 
} 
 
class Base 
{ 
    public static var test:String = "static"; 
} 
 
class Extender extends Base 
{ 
    public var test:String = "instance"; 
    public function Extender() 
    { 
        trace(test); // output: instance 
    } 
     
}