Mint már említettük, az objektumok kommunikációja kizárólag üzeneteken keresztül történik, a kifejezések csak ezekből épülnek fel. Nézzük most ezt közelebbről!
fogadóobjektum üzenetnév1 argumentum1 üzenetnév2 argumentum2 ...
A fogadóobjektum amelynek az üzenetet elküldjük, amelytől elvárjuk, hogy valami műveletet hajtson végre ennek hatására. Az üzenet neve állhat egy vagy több részből: a fenti példán az üzenetnév1 és üzenetnév2 ugyanannak az üzenetnek két darabja. Minden darab után egy objektumnak mint argumentumnak kell állnia, kivételt képezhet olyan üzenet, amely neve egy részből áll. Az üzenet teljes nevét szelektornak is szokták nevezni. Maga az üzenet tehát meghatározza a fogadót, a szelektort, és az argumentumokat. Ezek után az üzenetek három csoportját különböztetjük meg:
Ha az üzenetnek egyetlen implicit argumentumot adunk át, a fogadó objektumot, akkor unárisnak nevezzük. Az üzenet neve ekkor megfelel az azonosítókkal szemben támasztott követelményeknek, és illik kisbetűvel kezdeni. A hívás formája ekkor nagyon hasonlít a más nyelvekből megszokott szintaktikára. Az objektum neve után egy szóközzel (nem ponttal) elválasztva írjuk a metódus nevét. Például az alábbi utasítás eredményeképpen az i változóban tárolt szám abszolút értékét kapjuk meg: i abs
Ha egy argumentumot akarunk küldeni, és az üzenet neve a műveleti jelekből áll össze, akkor bináris üzenetről beszélünk. Első ránézésre ezek infix jelölésű műveleteknek tűnnek, de természetesen ezek is üzenetek. Az alábbiakban felsoroljuk a gyakrabban használt műveleteket:
Aritmetikai üzenetek | ||
---|---|---|
+ | Összeadás | |
* | Szorzás | |
/ | Racionális osztás (Az eredmény egy Fraction típusú racionális szám.) | |
// | Egész osztás | |
\\ | Modulusképzés | |
% | Modulusképzés, a másik irányba történő kerekítéssel | |
** | Hatványozás | |
<< | Bitenkénti lépetetés balra | |
>> | Bitenkénti lépetetés jobbra | |
Összehasonlító üzenetek | ||
< | Kisebb | |
> | Nagyobb | |
<= | Kisebb-egyenlő | |
>= | Nagyobb-egyenlő | |
= | Egyenlő | |
~= | Nem egyenlő | |
== | Azonos (ugyanarra az objektumra mutat) | |
~~ | Nem azonos | |
Logikai | ||
| | Logikai vagy | |
& | Logikai és | |
Egyéb | ||
, | Konkatenálás | |
@ | Számpár képzése (2D-pont) |
Azonban ezek csak a leggyakoribb bináris üzenetek, hiszen mi is bármilyent létrehozhatunk, és bármilyen műveletet rendelhetünk hozzá. Csupán a névre van pár megkötés:
Az alábbi példában a 3 objektumnak küldjük a + üzenetet a 4 paraméterrel: 3 + 4. Az eredmény a 7 egész számot tartalmazó objektum.
Ha explicit paramétereket is átadunk a metódusnak, akkor egy kicsit érdekesebb szintaxist kell használnunk. A metódust a paraméternevek azonosítják, azaz híváskor csak "paraméternév: paraméterérték" párokat sorolunk fel. Az üzenet szelektora ekkor a paraméternevek konkatenációjából adódik, pl.: #név1:név2:név3:. Példák:
Mi történik egy üzenet küldésekor? Először az üzenetet fogadó objektum, majd az üzenet, végül pedig a paraméterek kiértékelése történik meg (ez utóbbi rekurzívan). Az üzenet alapján végrehajtandó metódus meghatározása az öröklődési hierarchiában felfelé haladva történik. Ha az Object osztályig eljutottunk, és annak sincs ilyen üzenete, akkor runtime error lép fel. A metódusoknak mindig van visszatérési értéke, ami alapértelmezés szerint, maga az objektum. Ilyen módon az üzenetek egymásba ágyazhatóak: az első eredményeképpen kapott objektumnak újabb üzenetet küldhetünk. Pl: ((list at: 42)+1) tan sqrt rounded.
A Smalltalk szereti erősen megkülönböztetni az objektumnak küldött üzenetet az ennek hatására végrehajtott metódustól, bár erről a programozónak általában nem kell tudnia.
Az üzenetek kiértékelési sorrendjét az alábbi szabályok határozzák meg:
Ez a kiértékelés kissé szokatlan, ezért nézzünk néhány példát rá:
Kifejezés: | Értelmezése: | Amit nem jelent: |
---|---|---|
index + offset * 2 | ( index + offset ) * 2 | index + ( offset * 2 ) |
2 * theta sin | 2 * ( theta sin ) | ( 2 * theta ) sin |
bigFrame width: smallFrame width * 2 | bigFrame width: ( ( smallFrame width ) * 2 ) | ( ( bigFrame width: smallFrame ) width ) * 2 |
frame scale: factor max: 5 | frame objektumnak a #scale:max: üzenetet küldjük | frame scale: ( factor max: 5 ) ( frame scale: factor ) max: 5 |
Az ugyanarra az objektumra vonatkozó, egymást követő metódushívások összevonhatók egy sorozatba a pontosvessző segítségével. Tehát, ha egy üzenetben nem szerepel a fogadó neve, és az előző kifejezést egy pontosvessző zárja le, akkor a fogadó az előző kifejezésben utoljára elküldött üzenet fogadója lesz (nem pedig az üzenet válasza). Akkor használjuk, ha ugyanannak az objektumnak több üzenetet akarunk küldeni, és nem érdekel minket az üzenetek válaszai. Például:
A yourself hatására a t nem az utolsó üzenet eredményét kapja értékül, hanem a tömböt.
1+2*4;*5 eredménye 15, hiszen legutoljára a *4 metódust hajtottuk végre a 3 objektumra (ami az 1+2 eredménye volt).