2009-12-17

SCJP-6 06. Statiniai metodai per nuorodą

Turime kodą:
void m1() {
  Integer z = null;
  z.toString();  // NullPointerException
}

void m2() {
  Integer z = null;
  z.toString(12345);  // ok
}

Kodėl vykdant m1 kils NullPointerException, o m2 -- nekils? Antru atveju toString metodas yra ne objekto metodas, o statinis (klasės) metodas toString(int) su argumentu 12345. Kompiliavimo metu kreipinys z.toString(...) pakeičiamas kreipiniu Integer.toString(...) todėl vykdymo metu antru atveju problemų nekyla. Tačiau paprastai rekomenduojama statinius metodus kviesti naudojant klasės pavadinimą (taip pabrėžiant, kad metodas priklauso klasei, o ne objektui). Taigi m2 metodo kodas tvarkingiau atrodytų taip:
void m2() {
 Integer.toString(12345);
}

Egzamine, žinoma, gali pakliūti ir klausimų, kuriuose statiniai metodai kviečiami naudojantis nuorodos tipo kintamuoju, ir atvirkščiai -- naudojant klasės pavadinimą, bandoma iškviesti objekto metodą. Tad pravartu atsiminti, kokie yra dažniausiai sutinkamus metodai -- statiniai, ar objekto.

2009-12-05

SCJP-6 05. ref instanceof Type[]

instanceof operatorius skirtas patikrinti, ar tam tikras nuorodos (reference) tipo kintamasis saugo reikšmę į vieno ar kito tipo objektą (tikrinamas IS-A ryšys). Pavyzdžiui:
Object o = new ArrayList();  // sukuriamas naujas objektas
boolean isList = o instanceof List;  //  reikšmė TRUE
boolean isArrayList = o instanceof ArrayList;  //  reikšmė TRUE
boolean isLinkedList = o instanceof LinkedList;  // reikšmė FALSE
boolean isString = o instanceof String;  // reikšmė FALSE
boolean  isRunnable = o instanceof Runnable;  //  reikšmė FALSE
boolean isSerializable = o instanceof Serializable; // reikšmė TRUE

Aukščiau pateiktame pavyzdyje o yra apibrėžtas kaip Object tipo kintamasis, todėl kompiliatorius kintamojo o tikrinimui netaiko jokių ribojimų: galima lyginti su visais klasių hierarchijoje esančiais tipais. Jei kintamasis o būtų apibrėžtas kaip List tipo nuoroda, kompiliavimo metu kiltų klaidos eilutėse 5, 7. Kompiliavimo metu kompiliatorius patikrina, ar tikrinamasis kintamasis ir taikomasis tipas priklauso tai pačiai objektų hierarchijos šakai. Kadangi tipas Object yra "pačiame viršuje", šio tipo kintamuosius galima lyginti su bet kokiais klasių, interfeisų, masyvų ir Enum tipais (išskyrus anonimines klases, nes šių tipo tiesiogiai referuoti negalima).
Lyginimas su klasėmis nesudėtingas -- formatas
Serializable s = new Integer(123456);
boolean isInteger = s instanceof Integer;
Norint patikrinti, ar kintamasis rodo į masyvo tipo objektą, naudojama tokia sintaksė:
Object a = new int[10];  // masyvai yra objektai!
boolean isIntArray = a instanceof int[];  //  TRUE
boolean isIntegerArray = a instanceof Integer[];  //  FALSE

Čia svarbu "momentas": masyvai gali būti polimorfiški. T.y. masyvo iš k tipo elementų kintamajam galima priskirti visus masyvus iš m tipo elementų, kai m tenkina "IS-A" (yra) ryšį k tipo atžvilgiu. Pavyzdžiai:
Number [] ni = new Integer[10];  // legalus priskyrimas, nes Integer paveldi iš Number
boolean isIntegerArray = ni instanceof  Integer[];  // TRUE
boolean isNumberArray = ni instanceof Number[];  //  TRUE
boolean isObjectArray = ni instanceof Object[];  //  TRUE
boolean isLongArray = ni instanceof Long[];  //  FALSE -- Integer nėra bendroje šakoje su Long
boolean isStringArray = ni instanceof String[];  // kompiliavimo klaida, nes String  nėra bendroje hierarchijos šakoje su Number, todėl kompiliatorius šią situaciją sugeba aptikti

2009-12-03

SCJP-6 04. Throwable, Collection, Comparator

Egzaminui labai svarbu atsiminti, kad Throwable yra klasė, tuo tarpu Collection ir Comparator -- interfeisai. Pagal kai kurias rekomendacijas (pvz. žymiojoje Sierra ir Bates knygoje "SCJP for Java 6 Exam"), klases siūloma žymėti daiktavardžiais, o interfeisus -- būdvardžiais. Kai kuriais atvejais Java API taip ir daroma: klasės -- Object, String, Date, Calendar (nors tai ir abstrakti, tačiau -- klasė). Interfeisai: Comparable, Serializable, Runnable. Tačiau Throwable, Collection ir Comparator (bei kitais -- Set, List, Queue, Map ir pan.) atvejais nesusipainiokit. Atminkit, kad interfeisai neturi konstruktoriaus (todėl objekto nepavyks sukurti naudojant new, išplėsti klase (tik interfeisu) ar kviesti statinius metodus. Lygiai taip pat klasės negali būti išplėstos interfeisu ar implementuotos. Žemiau -- blogų pavyzdžių rinkinukas
//BLOGI pavyzdžiai
List list = new List(); // nėra konstruktoriaus, interfeisas
class MyClass extends Collection {...} // bandoma išplėsti interfeisą klase
Collection.sort(...); // interfeisas neturi statinių metodų, supainiota su klase Collections

interface MyThrowable extends Throwable {} // bandoma interfeisu išplėsti klasę
class MyThrowable implements Throwable {} // bandoma implementuoti klasę