Gyakorlati alapok II.
Saját kivételosztály
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:
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:
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:
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