12.4. Az Object osztály metódusai

Az Object osztály minden osztály közös őse, az osztályhierarchia tetején áll. Minden osztály közvetlen vagy közvetett módon utódja az Object osztálynak, így minden osztály rendelkezik az Object osztály metódusaival. Ez az osztály definiálja azt az alapvető működést, mely minden objektumnál rendelkezésre áll.
Az Object osztály által nyújtott legfontosabb metódusok a következők:

  • clone
  • equals és hashCode
  • finalize
  • toString
  • getClass

Ezeket sorra tárgyaljuk a következőkben.

12.4.1. A clone metódus

A clone metódust akkor használjuk, ha létre szeretnénk hozni egy objektumot, egy már meglévő objektumból (másolatot készíteni róla). Az adott osztállyal megegyező típusú új példányt hoz létre:

aCloneableObject.clone();

A metódus a CloneNotSupportedException kivételt dobja, ha a klónozás nem támogatott az osztály számára. A klónozás akkor támogatott, ha az osztály implementálja a Cloneable interfészt. Habár az Object tartalmazza a Clone metódust, nincsen megvalósítva az interfész. Ha az objektum, ahol a clone-ra hivatkoztunk, nem implementálja a cloneable interfészt, egy eredetivel azonos típusú és értékű objektum jön létre. Legegyszerűbb azonban, ha az osztály deklarációban létrehozunk egy implements Cloneable sort.

Bizonyos osztályoknál a helyes működés feltétele a clone felüldefiniálása. Tekintsünk egy Stack osztályt, mely tartalmaz egy tagváltozót, mely az Object-ek tömbjére hivatkozik. Ha a Stack az Object osztály clone metódusára épül, akkor az eredeti és a másolt Stack ugyanazokat az elemeket fogja tartalmazni, mivel az adattag tömb, és másoláskor csak referencia másolás fog történni.

A Stack osztálynak olyan clone implementációra van szüksége, amely lemásolja a Stack objektum adattagjait, ezzel biztosítva a megfelelő tartalom szétválasztást:

public class Stack implements Cloneable {
    private Object[] items;
    private int top;
    ...
    protected Stack clone() {
        try {
            Stack s = (Stack)super.clone(); //clone the stack
            s.items = (Object)items.clone(); //clone the array
            return s; // return the clone
        } catch (CloneNotSupportedException e) {
           //This shouldn't happen because Stack is Cloneable.
            throw new InternalError();
        }
    }
}

Az implementáció viszonylag egyszerű. Először a clone metódus Object implementációja hívódik meg a super.clone segítségével, mely létrehoz és inicializál egy Stack objektumot. Ilyenkor mindkét objektum ugyanazokat az objektumokat tartalmazza. Ezután a metódus lemásolja az objektumokat, és a metódus Stack-el tér vissza.

Megjegyzés: A clone metódus nem a new-t használja a másolat létrehozásánál és nem hív konstruktorokat, helyette a super.clone-t használja, mely létrehozza az objektumot a megfelelő típussal, és engedélyezi a másolást, minek eredményeképpen a kívánt másolatot kapjuk.

Érdemes még azt is megfigyelni, hogy az adattag másolását sem „kézzel” végezte a metódus, hanem a tömb objektum clone metódusával. Ez a metódus egy másik azonos méretű tömböt hoz létre, aminek elemeiről is másolat készül. (A tömbben tárolt tagokról már nem fog másolat készülni, de ez nem is célja egy verem másolásnak.)

12.4.2. Az equals és hashCode metódusok

Az equals metódus két objektumot hasonlít össze és dönti el, hogy egyenlők-e vagy sem (ha egyenlők, true-val tér vissza). Ha önmagával hasonlítunk egy objektumot, true-t ad vissza.

A következő programrészlet összehasonlít két Integer-t:

Integer one = new Integer(1);
Integer anotherOne = new Integer(1);
if (one.equals(anotherOne)) {
    System.out.println("objects are equal");
}

A program kimenete:

objects are equal

Egyenlők, mivel az értékük megegyezik. Ha két objektum egyenlő az equals metódus szerint, akkor a hashCode metódus által szolgáltatott értékeknek is meg kell egyezniük. (Figyelem, fordítva ez nem feltétlenül igaz!)

Ha az equals működése nem megfelelő az osztályunk számára, akkor felül kell írnunk az osztályunkban.

A hashCode metódus állítja elő az objektumok hash kódját, ami például akkor lehet szükséges, ha az objektumot hashtáblában tároljuk. Hash kódként (a metódus visszatérési értékeként) mindig egy int típusú számot kapunk.

Helyes hash függvény írása egyszerű, azonban hatékony függvény írása nehéz lehet, komolyabb munkát igényel. Ez a téma azonban már nem fér bele a jegyzetünkbe.

12.4.3. A finalize metódus

Az Object osztály ugyancsak tartalmazza a finalize metódust. A szemétgyűjtő meghívja, ha már nincs egyetlen hivatkozás sem az objektumra. A finalize metódus automatikusan meghívódik, melyet a legtöbb osztály használ, ezért nem is kell külön meghívni.

A finalize metódussal legtöbbször nem kell törődnünk, az őstől örökölt metódus többnyire megfelelően működik.

12.4.4. A toString metódus

Az objektumot String-ként ábrázolja. Hasznos minden új osztály definíciója során felülírni, hogy a megfelelő értékeket reprezentálhassa. Használhatjuk a toString-et a System.out.println-nel együtt az objektumok szöveges megjelenítésére, pl.:

System.out.println(new Double(Math.PI).toString());

A futás eredménye:

3,14159

Nagyon hasznos ez a metódus akkor, ha a program tesztelési fázisában bizonyos objektumok tartalmát ellenőrizni szeretnénk. Ilyenkor csak ki kell írni a kérdéses objektumot, például a konzolra:

System.out.println(anObject);

12.4.5. A getClass metódus

Visszaadja a futásidejű osztályát az objektumnak. Az Object osztály nem engedi meg a getClass metódus felüldefiniálását (final).

A következő metódus az objektum osztálynevét jeleníti meg:

void PrintClassName(Object obj) {
    System.out.println("The Object's class is "
                       + obj.getClass().getName());
}

A következő példa létrehoz az obj típusával megegyező másik objektum példányt:

Object createNewInstanceOf(Object obj) {
    return obj.getClass().newInstance();
}

Ha tudjuk az osztály nevét, kaphatunk egy Class objektumot az osztálynévből. A következő két sor egyaránt ugyanazon végeredményt produkálja (a második változat hatékonyabb):

String.class
Class.forName("String")