Az ELASTIC programozási nyelv

Objektum orientáltság

Az elastiC objektum orientáltságának alapja az osztály, amit a class kulcsszó vezet be.

 
    SCOPE class NAME extends SUPERCLASS
    {
        CLASS_STATEMENT
        ...
    }

Ezzel a szintaxissal hozhatunk létre egy új objektummintát. Egy osztály láthatósága SCOPE ugyan úgy kétféle lehet, mint az alprogramoknál láttuk, és a jelentéstartama is ugyanaz, tehát egy public kulcsszóval deklarált osztály más csomagokból is látszik, belőle bárhol példány hozható létre, ahol az import részben felsorolták azt a csomag nevét, amelyben az osztály deklarálva van. Ezzel szemben a private osztályok csak a saját csomagjukon belülről láthatóak.

Adattagok

Bármely változó, amit az osztály törzsében hozunk létre, private változó lesz, és így csak az osztály belsejében látszik. A külvilág csak metódusokon keresztül érheti el, és módosíthatja ezeket. Kétféle módosítószóval deklarálhatunk az osztály törzsében változókat. A statikus változókat static kulcsszó segítségével hozhatunk létre, ezekből nem jön létre új példány az osztály példányosításakor, de minden ilyen típusú objektum elérheti, és módosíthatja őket, ezek a változók az osztály részét képezik. A másik lehetőség a példányváltozó, amelyet a local foglalt szó vezet be. Ezekből a változókból minden példány saját egyeddel rendelkezik. És ezeket a változókat csak az adott példány metódusai érhetik el, az osztály szintű metódusok nem. Adattagoknak értéked adni, csak metódusokon keresztül lehet, tehát az alábbi kód hibás.

 
        public class Minden extends basic.Object
        {
                local valasz = 42; //hibás értékadás
        }

Létezik minden objektumnak két előre definiált adattagja, az önmagára mutató referencia a self, és a közvetlen ősre mutató referencia a super.

Metódusok

A metódusoknak ahogyan az adattagoknak is két fajtája van. Ellentétben a globális függvényekkel, és a C/C++-al itt a method kulcsszó vezeti be az osztály objektumaihoz tartozó műveletek definícióját. Az osztályszintű műveletek pedig a class módosítószóval kell ellátni.

 
        public class Valami extends basic.Object
        {
               //...
               method Alma()  //példány szintű metódus
               {
               }
               class method StaticAlma()      //osztályszintű metódus
               {
               }
               //...
               
        }

A metódusok láthatósága az adattagokkal ellentétben mindig publikus. A metódusok paraméterlistájának megadására kétféle szintaxist biztosít a nyelv. Az első módszer megegyezik a globális függvényeknél látottaknál, és az előző példában is ezt a szintaxist láthattuk. A második lehetőség bemutatására álljon itt egy újabb példa.

 
        method drawLineFrom: pointA To: pointB
        {
        }

a metódus hívása:

	[obj moveToX: 30 Y: 20];

Ebben a deklaráció a következővel ekvivalens, de kifejezőbb annál.

 
        method drawLineFrom:To:(pointA, pointB)
        {
        }

a metódus hívása:

	[obj moveTo 30, 20];

Mindkét módszer esetében használhatjuk a változó hosszú paraméterlista jelölésére a ... konstrukciót. Ezek a meglátások mind érvényesek a class method kulcsszó párossal bevezetett osztály szintű metódusokra is.

A metódushívások egymásba ágyazhatóak:

	[[obj getMainObject] moveToX: 30 Y: 20];

vagy pl.

	[obj moveToX: 30 Y: [obj getCurrentY]];

A metódusokban meghívatunk más metódusokat is ugyanazon az objektumon a self használatával:

	method draw()
	{
		// a keret kirajzolása
		[self drawBorder];

		// a belsejének kirajzolása
		[self drawInside];
	}

A super kulcsszó segítségével az is lehetséges, hogy a szülőosztály metódusait hívjuk meg:

    method draw()
    {
        // a szülő osztály draw metódusának meghívása:
        [super draw];

        // az egyedi dolgok kirajzolása
        ...
    }

Megjegyezzük, hogy ha ebben a példában self -et használunk a super helyet, akkor egy végtelen rekurzióba kerülünk!

A metódusok változókra való hivatkozásának szabályai

- a közönséges ( objektum szintű) metódusok hivatkozhatnak a példány változókra és az osztályszintű változókra
- az osztály szintű metódusok hivatkozhatnak az osztályszintű adattagokra ( a nevük használatával)
- egyébként a (objektum és osztályszintű) metódusokra a szokásos láthatósági szabályok vonatkoznak, amikor a közönséges (globális) változókra hivatkoznak
- a példány és osztály változók elsőbbséget élveznek a közönséges változókkal szemben: ha egy változó az osztályban és globálisan is definiált, akkor az osztályban definiált változó látható
- a közönséges metódusok hivatkozhatnak a beépített self változóra, jelezve hogy az objektum fogadja a metódus hívást
- a metódusok hivatkozhatnak a beépített super változóra, jelezve hogy a közvetlen szülőosztály fogadja a metódus hívást ( mint a this a C++-ban)

Használat

Minden osztályból a new kulcsszó segítségével hozhatunk létre új objektumpéldányt. Ilyenkor létrejön egy új objektum, majd lefut az osztály konstruktora, ami nem más, mint egy init nevű metódus, ha mi nem írtunk ilyet, a fordító létrehoz egyet üres paraméterlistával és üres törzzsel. Lehetőség van tetszőleges paraméterlistájú init írására, de minden osztály csak egy konstruktort tartalmaz, mert a nyelv a metódusok túlterhelését sem támogatja. Egy objektum műveletének meghívása "üzenetküldéssel" történik, amit a következő példa jól illusztrál.

 
        public class Point extends basic.Object
        {
               method init()
               {
                       x = 0;
                       y = 0;
               }
               method MoveTo(a,b)
               {
                       x = a;
                       y = b;
               }
               local x;
               local y;
        }
 
        private point = [new Point];
        [point MoveTo 1, 2];

Operátorok

A nyelv lehetőséget biztosít az operátorok túlterhelésére az osztályokban, mégpedig úgy, hogy a basic.Object osztályban definiált, az egyes operátoroknak megfelelő függvényeket felüldefiniáljuk.

 
        public class Hash extends basic.Object 
        {
               method _getitem( key ) 
               {
               //...
               }
 
               method _setitem( key, val )
               {
               //...
               }
        }
 
        private ht = [Hash new];
 
        ht["Hello"]  = "World";               //_setitem(key, val)
        basic.print( ht["Hello"], '\n' );     //_getitem(key)

Öröklődés

Minden osztály közvetve vagy közvetlenül a basic.Object osztály leszármazottja. Minden osztálydeklarációnál kötelező feltüntetni az ősosztályát, ha ilyen nincs, akkor az basic.Object-et. A nyelv csak egyszeres öröklődést tesz lehetővé, és nem támogatja az interface képzést. A nyelvben nem kell jelölni azokat a tagfüggvényeket, amelyeket később felül akarunk definiálni, ebből következik, hogy bármely ősosztálybeli függvény felüldefiniálható egy leszármazottban. A jobb szemléltetés végett álljon itt a megszokott alakzatos példaprogram az öröklődésre.

 
        package shapes;
 
        import basic;
        import array;
 
        public class Shape extends basic.Object
        {
               // instance variables
               local name;
               local color;
 
               // methods
        
               // a method with keywords
               method setName: shapeName andColor: shapeColor
               {
                       name  = shapeName;
                       color = shapeColor;
               }
 
               // a method with function-like name
               method print()
               {
                       basic.print( [[self isA] name],   "\n",
                                               "   name : ", name,  "\n",
                                               "   color: ", color, "\n",
                                               "   area : ", [self getArea], "\n" );
               }
        }
        
        public class Rectangle extends Shape
        {
               local w, h;
        
               // initializer (called by new)
               method init( name, color, width, height )
               {
                       [super setName: name andColor: color];
        
                       w = width;
                       h = height;
               }
        
               method getArea()
               {
                       return w * h;
               }
        }
        
        public class Circle extends Shape
        {
               local r;
        
               // initializer (called by new)
               method init( name, color, radius )
               {
                       [super setName: name andColor: color];
        
                       r = radius;
               }
        
               method getArea()
               {
                       return 3.1415 * r * r;
               }
        }
        
        public class Square extends Rectangle
        {
               // initializer (called by new)
               method init( name, color, side )
               {
                       [super init name, color, side, side];
               }
        }
        
        // a Shape container
        
        public class ShapeBag extends basic.Object
        {
               local bag;
        
               // initializer (called by new)
               method init()
               {
                       // an empty array
                       bag = #[];
               }
        
               method addShape( shape )
               {
                       array.push( bag, shape );
               }
        
               method print()
               {
                       basic.print( "*** Shape Bag: ***\n\n" );
        
                       local shape;
                       local totalArea = 0;
                       for (shape in bag)
                       {
                               [shape print];
                               totalArea = totalArea + [shape getArea];
                       }
        
                       basic.print( "Total area: ", totalArea, "\n" );
               }
        }
        
        // main code
        
        private bag = [ShapeBag new];
        [bag addShape [Rectangle new "r1", "red",    30, 20]];
        [bag addShape [Rectangle new "r2", "green",  10, 15]];
        [bag addShape [Square    new "s1", "blue",   10]];
        [bag addShape [Circle    new "c1", "brown",  10]];
        [bag addShape [Circle    new "c2", "yellow", 15]];
        [bag print];