Kihagyás

11. előadás

Egyenlőségvizsgálat

Az == objektum azonosságot vizsgál

Time t1 = new Time(13, 30);
Time t2 = new Time(13, 30);
System.out.println(t1 == t2); //Nem így van, hiszen ez a REFERENCIÁKAT hasonlítja össze, nem az értékeket
t2 = t1;
System.out.println(t1 == t2);

Helyette az equals függvényt használjuk (tartalmi egyenlőség vizsgálat):

Time t1 = new Time(13,30);
Time t2 = new Time(13,30);

System.out.println( t1 == t2 ); // false

System.out.println( t1.equals(t2) ); // true

Az equals függvény az Object-ben van definiálva (tehát minden objektumból elérhető)

public class Object {
    //...
    public boolean equals( Object that ){ 
        // ...
    }
}

Felüldefiniálás

Az equals szerződés betartandó:

  • Ekvivalencia-reláció
  • Ha a != null, akkor !a.equals(null)
    • Viszont null.equals(a) \(\Rightarrow\) NullPointerException
  • Konzisztens a hashCode() metódussal
    • egyenlő objektumok hashCode-ja egyezzen meg
    • különböző objektumok hashCode-ja jó, ha különböző

Alapértelmezetten az Equals csak objektumvizsgálatot csinál

A fordító nem ellenőrzi

Szabályos felüldefiniálás

public class Time {
    @Override
    public boolean equals( Object that ){
        // "Gyorsító feltétel": Ha a két objektum "object equal", akkor TrIvIáLiSaN megegyeznek
        if(this == that) return true;

        // `that` nem null, és Time-ból származik le
        if( that != null
              && getClass().equals(that.getClass()) ){
            Time t = (Time)that;
            // Mezők megegyeznek
            return hour == t.hour && minute == t.minute;
        } else {
            return false;
        }
    }

    @Override public int hashCode(){ return 60*hour + minute; }
}

Jellemző hiba

Jellemző fordítási hiba az @Override-nak köszönhetően

  • Minkénképp Object kell, hogy legyen a paramétere
  • Amikor a Time-osat használjuk, akkor valójában túlterhelés történik
  • Elhagyható a @Override, de rossz gyakorlat
    • ("Tessék kiírni és nem bonyolítani a kollégák dolgát... Valamint a fordító tud szólni, ha probléma van.")
public class Time{

    @Override // Override nem lehetséges, mert a szignatúra csak akkor jó, ha a paraméter Time.
              // Elhagyni sem érdemes az Override-ot, mert több problémát okozhat, mint megold
    public boolean equals( Time that ){
        return that != null && hour == that.hour && ...
    }

    @Override public int hashCode(){
        return 60 * hour + minute;    
    }
}

Túlterhelés

"This is a bad idea"

~Kozsik

static void connect( Employee e, Manager m ){
m.addUnderling(e);
}

static void connect( Manager m, Employee e ){
m.addUnderling(e);
}

Bármilyen sorrendben megadhatóak az Employee-k és a Manager-ek

A nyelv ezt is elfogadja

DE! Két Manager esetén nem mondható meg, hogy melyik metódust kéne lehívni, fordítási hiba.

Helyette lehetne connect(mike,(Employee)mary);, de az se túl szép.

Tanulság: Soha ne terheljünk túl altípuson!


LadyJava


Öröklődés és equals

public class ExactTime extends Time { ...
    // A megörökölt `equals`-t felhasználja
    @Override
    public boolean equals( Object that ){
        return super.equals(that) && second == ((ExactTime)that).second;
    }
    // A megörökölt `hashCode`-ot felhasználja
    @Override
    public int hashCode(){
        return 60*super.hashCode() + second;
    }
}

instanceof, getClass ... equals helyett

public class Time {
    //...
    @Override
    public boolean equals( Object that ){
        if( this == that ) return true;
        if( that instanceof Time ){
            Time t = (Time)that;
            return hour == t.hour && minute == t.minute;
        } else return false;
    }
    @Override
    public int hashCode(){ return 60*hour + minute; }
}

Ez akkor is működik, ha that altípusa Time-nak, DE problémákat okozhat (pl. a másodpercek egyenlőségét nem teszteli).

final metódus

  • nem definiálható felül
public class Time {
    // ...
    @Override
    public final boolean equals( Object that ){
        if( that instanceof Time ){
            Time t = (Time)that;
            return hour == t.hour && minute == t.minute;
        } else return false;
    }
}
ExactTime e1 = new ExactTime(11,22,44);
ExactTime e2 = new ExactTime(11,22,33);

e1.equals(e2)  // RST, de nem ,,igazi" egyenlőség

final class

  • Nem lehet belőle leszármaztatni
  • Nem lehet specializálni, felüldefiniással belepiszkálni, elrontani
  • Módosíthatatlan (immutable) esetben nagyon hasznos
  • java.lang.Class, java.lang.Integer és egyéb csomagoló osztályok, java.math.BigInteger stb.
package java.lang;
public final class String implements ... { ... }

HashSet