Gyakorlati alapok II.
Külön (nem rendőrségi) eljárás
Az eljárás és függvény fogalmainak tisztázása már alapszinten megtörtént az Adat, művelet, metódus, osztály, csomag, objektum című fejezetben. Fontossága miatt a vonatkozó szöveget ide másolom:
A metódus bemeneti adato(ka)t és legtöbbször több műveletet foglal magába, valamint általában egy, de a műveletnél magasabb szintű probléma megoldására hivatott. Ilyen lehet például a százalékszámítás, amely során 2, esetleg 3 bemeneti adat felhasználásával osztanunk és szoroznunk is kell (lásd az alábbi, nem futtatható Java-kódot).
A metódus további fontos tulajdonsága, hogy lehet 1 visszatérési értéke (return), amely voltaképpen a metódus egyetlen kimeneti adata. (A metódusnak lehet több kimeneti adata is, ha összetett típusba, például tömbbe van csomagolva.) Ezzel valójában nem mondtunk újat, mert a cél mindig is a helyes kimeneti adat produkálása volt, de ha megtesszük, hogy a metódust egyetlen, kívülről nem, vagy csak nagyon szabályozott feltételekkel módosítható egységbe zárjuk, akkor programozás-technikailag nagyot léptünk egy új, még fejlettebb irányba, ez pedig az objektumorientált programozás.
Az újfajta metódusszemlélet azonban még tovább fokozható: számos metódust előre megírhatunk és később könnyen felhasználhatjuk őket, ha pedig rendszerezve lesznek, akkor egy elképesztően praktikus „programozási közkönyvtárrá” fognak válni.
Az alábbi futtatható Java-kódban egy százalékszámító metódus 2 bemeneti paramétert vár, amelyből 1 kimeneti adatot állít elő. Ez az előre gyártott kód azonban bármelyik projektbe megfelelően beillesztve azonnal képes százalékszámításra:
public class Main {
public static double szazalekSzamitas(double szazSzazalek, int szazalek){
double eredmeny = 0;
double egySzazalek = szazSzazalek / 100;
eredmeny = egySzazalek * szazalek;
return eredmeny;
}
public static void main(String[] args) {
double szazSzazalek = 50;
int szazalek = 20;
double eredmeny = szazalekSzamitas(szazSzazalek, szazalek);
System.out.println(eredmeny);
}
}
Végeredmény:
10.0
A fenti algoritmus kissé tömörebben:
public class Main {
public static double szazalekSzamitas(double szazSzazalek, int szazalek){
return (szazSzazalek / 100) * szazalek;
}
public static void main(String[] args) {
double szazSzazalek = 50;
int szazalek = 20;
System.out.println(szazalekSzamitas(szazSzazalek, szazalek));
}
}
Végeredmény:
10.0
Néhány régebbi programozási szakkönyvekben a metódus, mint fogalom kétféle formában kerül definiálásra:
-
függvény – olyan metódus, amelynek van önálló típusa, vele kimeneti értéke (return),
-
eljárás – olyan metódus, amelynek nincs kimeneti értéke (void).
Újabb szakkönyvekben már nem találkoztam ilyen definícióval, noha a metódus szinonimájaként ezekben is használatosak az említett szavak, főként a függvény. Példák:
-
a Pascal az eljárásra procedure, a függvényre function szóval,
-
a Visual Basic az eljárásra sub, a függvényre function szóval hivatkozik.
A fenti definíciók tehát remek gyakorlati kiindulópontként szolgálhatnak néhány példához. Kezdjük a példálózást az eljárással!
Ismétlésképpen: az eljárás olyan metódus, amelynek nincs kimeneti értéke (void).
Először vegyünk egy üres main() függvényt, amint azt tettük például A main() tulajdonságai című fejezetben:
public class Main {
public static void main(String[] args) {
}
}
Ugyan ez a függvény nem csinál semmit, de már megvan a működéshez szükséges kódkeret. Írjunk ki egy hibaüzenetet a konzolra:
public class Main {
public static void main(String[] args) {
System.out.println("Ez egy
hibaüzenet!");
}
}
Végeredmény:
Ez egy hibaüzenet!
Láthatjuk: a hibaüzenet a főprogramon (main) belül van. Most pedig ezt a hibaüzenetet tegyük ki a Main osztályon belül egy külön eljárásba. No, ez nem lesz zökkenőmentes!
public class Main {
public static void hibaUzenet(){
System.out.println("Ez egy
hibaüzenet!");
}
public static void main(String[] args) {
hibaUzenet();
}
}
Végeredmény:
Ez egy hibaüzenet!
A végeredmény ugyanaz, ám a kódban fontos újdonságok tapasztalhatók. Vegyük őket sorra:
-
a metódus működtető kódjai a Main osztályon belül, de a fejléc után a {} jelek közé kerülnek (itt: System.out.println("Ez egy hibaüzenet!");),
-
a metódus fejlécében (public static void hibaUzenet()) fontos szabványos deklarációk vannak:
-
public - ez egy hozzáférés-módosító (access modifier), azt jelenti, hogy a metódus kívülről, azaz más metódusok vagy osztályok felöl látható, elérhető, meghívható,
-
static - az osztály példányosításakor (itt: Main) ezzel a jelzővel illetett metódus vagy adattag szintén bekerül a műveleti memóriába (úgy mondják, hogy osztályszintű metódus, adattag), azaz azonnal elérhető, futtatható (Alapszinten a static módosítóról című fejezet),
-
void - ez azt jelenti, hogy üres, a metódusnak nincs visszatérési értéke, azaz valamilyen típusú kimeneti adata, hanem csupán elvégez egy célirányos feladatot,
-
hibaUzenet() - a szokásos névkonvenciók figyelembevételével (Adatok, metódusok elnevezésének problémái című fejezetben foglaltak szerint) a metódusok neveit mindig kisbetűvel kezdjük (camel case)!
-
Mivel a hibaUzenet() metódus static jelzővel illetett, a main futtatásának pillanatában azonnal létrejön (azaz példányosított), ezért a főprogramban elég csak a nevével hivatkozni rá:
public static void main(String[] args) {
hibaUzenet();
}
}
Ha azonban a static jelzőt elvesszük a Hibauzenet() metódus fejlécéből...
public void hibaUzenet()
...futtatás esetén a következő hibaüzenetet kapjuk:
Exception in
thread "main" java.lang.Error: Unresolved compilation problem:
Cannot make a static reference to the non-static method Hibauzenet() from the
type Main
at Main.main(Main.java:7)
Ezt azt jelenti, hogy mivel a hibaUzenet() metódus nem statikus, a main futtatásának pillanatában a JVM nem találja, mert kódja nincs a műveleti memóriában. E mögött egy egyszerű hozzáférési szabály húzódik meg:
Statikus változók és metódusok nem férhetnek hozzá NEM statikus változókhoz, metódusokhoz. Viszont NEM statikus változók és metódusok hozzáférhetnek statikus változókhoz, metódusokhoz, hiszen éppen ez a statikusság fő előnye.
Érdekességként az alábbi kódban megnézhetjük, hogy miként lehet kikerülni a fenti problémát, azaz miként tudjuk mégis elérni a Main osztályon belüli, de nem statikus metódust. Az eljárás hajmeresztő, alkalmazását nem javaslom, de érdekességnek megteszi:
Érdekes futtatható Java-kód! - Miként tudjuk elérni az osztályon belüli, de nem statikus metódust?