Gyakorlati alapok II.

Saját kivételosztály

www.informatika-programozas.hu - Ez a programozási feladat nehéz lesz!

 

Ez a fejezet garantáltan haladó anyagot tartalmaz, hiszen benne osztályokról és öröklődésről is szó esik, amely az osztályok közötti egyfajta származási-származtatási lehetőséget jelent. Ezen témakörök azonban csak a következő nagyobb fejezetcsomagban fognak ismertetésre kerülni (Gyakorlati alapok III. (Az osztályok)). Az ok, amiért mégis ide szerkesztettem, hogy így a kivételekkel foglalkozó fejezetcsomag szinte teljesen lefedi a témakört, azaz amit csak tudni érdemes a kivételkezelésről, az ebben az egységben elérhető lesz.

Az egyik előző, a Nyugodtan hozzám vághatod a kivételedet! (throw) című fejezetben beszéltünk már a kivétel továbbdobásáról. Ennek lényege, hogy ha lusta programozó nem kíván foglalkozni a kivételekkel, akkor nyugodtan továbbdobhatja másvalaki felé. A tréfás megjegyzés mögött voltaképpen kőkemény objektumorientált szemlélet rejtőzik, miszerint a funkciókat nagyon nagy alapossággal és előre átgondoltsággal kell szétválasztanunk egymástól. Ebből a szempontból az egyik legjobb megoldás, ha a kivételkezelést egyenesen külön osztályban végezzük el. Erre láthatunk példát az alábbi kódokban és rögtön nézzük is át a mechanizmust.

 

A Main osztály továbbdobja a kivételkezelést (throws InvalidNumberException), amely most egy egyszerű alsó-, és felsőhatár-ellenőrzéssel birkózik. A deklarálás után a rendszer azonnal visítozni kezd, hiszen nem találja a névhez (a referenciához) köthető kivétel-objektumot. Ezt nekünk kell (természetesen azonos néven) egy különálló osztályban létrehozni, illetve további újdonságként származtatnunk is kell az Exception ősosztályból, ezáltal “megörökölve” az ősosztály összes tulajdonságát. Lényeges momentum ez, hiszen kivétel-objektumot szándékozunk alkotni, amely formailag és tartalmailag is így viselkedik. Az osztály konstruktoráról, valamint a benne látható super() utasításról a Rombolás helyett építkezés: a new parancs és a konstruktor függvény és az Örököltetés: síkidom - kör - négyzet - téglalap a super() segítségével című fejezetekben olvashatunk.

Nem megfelelő számbekérés esetén (alsóhatár alatt és felsőhatár felett) meghívódik az általunk kreált kivétel-objektum és az általunk beállított viselkedés szerint fog lefutni (itt egy szöveges üzenet):

Végeredmény:
Kerem, hogy gepelje be a szamot!
11
Exception in thread "main" InvalidNumberException: A szam intervallumon kivuli!
at Main.main(Main.java:12)


Vegyük azonban észre, hogy van egy másik, “automata” kivételkezelésünk is, amely akkor fog aktiválódni, ha a bemeneti adatot nem sikerül értelmezhető számmá alakítani (InputMismatchException). Ráadásként a printStackTrace() függvénnyel a hiba keletkezésének helyét is megállapíthatjuk:

Végeredmény:
Kerem, hogy gepelje be a szamot!
g
java.util.InputMismatchException
Szambevitel nem sikerult!
at java.base/java.util.Scanner.throwFor(Unknown Source)
at java.base/java.util.Scanner.next(Unknown Source)
at java.base/java.util.Scanner.nextInt(Unknown Source)
at java.base/java.util.Scanner.nextInt(Unknown Source)
at Main.main(Main.java:10)


Nézzük meg a futtatható Java-kódot:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

Main.java

 

import java.util.*;

public class Main {

public static void main(String[] args) throws InvalidNumberException {
    System.out.println("Kerem, hogy gepelje be a szamot!");
    int szam = 0;
    Scanner scanner = new Scanner(System.in);
    try {
        szam = scanner.nextInt();
        if (szam < 0 || szam > 10) {
            throw new InvalidNumberException("A szam intervallumon kivuli!");
        }
    System.out.println("Szam OK!");
    }
    catch (InputMismatchException imme){
        imme.printStackTrace();
        System.out.println("Szambevitel nem sikerult!");
        }
    }
}
 

InvalidNumberException.java


public class InvalidNumberException extends Exception {

public InvalidNumberException(String message) {
    super(message);
    }
}

Kerem, hogy gepelje be a szamot!
11
Exception in thread "main" InvalidNumberException: A szam intervallumon kivuli!
at Main.main(Main.java:12)

 

A kód azonban enyhén szólva sem optimális, ezt tapasztalt programozók hamar észrevehetik. Egyrészről a kód validálási szempontból nem jó, hiszen benne (értsd: a validálási szempontokban) legtöbbször egyértelmű hierarchia állítható fel. Ebben a rövid kódban például rögtön 2 szempontot is le kell kezelnünk: a bemeneti String számmá konvertálhatóságát, valamint a szám alsó-, és felsőhatár ellenőrzését. Egyébként a hierarchiában ez a megfelelő sorrend, hiszen először meg kell néznünk, hogy a String egyáltalán konvertálható-e számmá, majd csak ezután vethetjük alá (bármilyen) értékellenőrzésnek. Ha megfigyeljük, a fenti kódban nem ez a sorrend, ezt tehát iziben korrigálnunk kell. A korrekcióhoz egy kicsit át kell dolgoznunk a kódot, de az lényegében azonos funkcionalitású marad:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

Main.java

 

import java.util.*;

public class Main {

public static void main(String[] args) throws InvalidNumberException {
    System.out.println("Kerem, hogy gepelje be a szamot!");
    int szam = 0;
    boolean szamOK = true;
    Scanner scanner = new Scanner(System.in);
    try {
        szam = scanner.nextInt();
    }
    catch (Exception e){
        szamOK = false;
        e.printStackTrace();
        System.out.println("Szambevitel nem sikerult!");
    }
    if (szam < 0 || szam > 10) {
        szamOK = false;
        throw new InvalidNumberException("A szam intervallumon kivuli!");
    }
    if(szamOK) {
        System.out.println("Szam OK!");
        }
    }
}
 

InvalidNumberException.java


public class InvalidNumberException extends Exception {

public InvalidNumberException(String message) {
    super(message);
    }
}

Végeredmény:

Kerem, hogy gepelje be a szamot!
5
Szam OK!

 

Sajnos a kód még ezután sem optimális, mert benne kétféle lokalizáltságú kivételkezelés zajlik. Hiszen ki látott olyan kivételkezelést, amelyben az egyik kivétel külön osztályban, míg a többi lokálisan van megoldva? Ilyen öszvérkódokért bizony nem fogunk kapni sem szakmai dicséretet, sem bónuszt, ezért hamarjában dolgozzuk is át, hogy az InputMismatchException nevű kivétel is külön osztályba kerüljön. Megoldása hasonló lesz a már ilyen módon lekezelt kivételünkhöz. Még annyi különbséget teszünk, hogy a hibaüzenetek is a saját osztályokba fognak kerülni, tehát a kódszegmenseket most már egymástól funkcionálisan tényleg teljesen elválasztottuk.

 

Nézzük meg a futtatható Java-kódot:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 

 

Main.java

 

import java.util.*;

public class Main {

public static void main(String[] args) throws InvalidNumberException, InputMismatchException {
    System.out.println("Kerem, hogy gepelje be a szamot!");
    int szam = 0;
    Scanner scanner = new Scanner(System.in);
    try {
        szam = scanner.nextInt();
    }
    catch (Exception e){
        throw new InputMismatchException();
    }
    if (szam < 0 || szam > 10) {
        throw new InvalidNumberException();
    }
    System.out.println("Szam OK!");
    }
}

 

InvalidNumberException.java


public class InvalidNumberException extends Exception {

public InvalidNumberException() {
    super();
    System.out.println("A szam intervallumon kivuli!");
    }
}

 

InputMismatchException.java

public class InputMismatchException extends Exception {

public InputMismatchException() {
    super();
    System.out.println("Szambevitel nem sikerult!");
    }
}
 

Végeredmény:

Kerem, hogy gepelje be a szamot!
d
Szambevitel nem sikerult!
Exception in thread "main" InputMismatchException
at Main.main(Main.java:13)

 

Természetesen a Main osztályban a kivétel továbbdobásánál mindkét osztályt meg kellett említenünk:

 

public static void main(String[] args) throws InvalidNumberException, InputMismatchException