pátek 21. března 2008

Používáte v equals metodě getClass a nebo instanceof?

Jak jinak začít, než střelbou do vlastních řad. Tak jsem byl pro změnu zase jednou za blbce... V několika třídách jednoho modulu jsme měli následující implementaci equals metody.

01   public boolean equals(Object obj) {
02     if (this == obj)
03       return true;
04     if (obj == null)
05       return false;
06     if (getClass() != obj.getClass())
07       return false;
08     final Foo other = (Fooobj;
09     if (something == null) {
10       if (other.something != null)
11         return false;
12     else if (!something.equals(other.something))
13       return false;
14     return true;
15   }
Java2html

Na první pohled nic zvláštního, kód bude fungovat. Až do chvíle kdy se rozhodneme, že třídu s takto naimplementovanou metodou equals začneme používat v Hibernatu resp. stane se z ní entity beana. V takovém případě nám možná řádek číslo šest způsobí nemalé problémy. Co se stane? Hibernate nám totiž může podstrakovat proxy objekty (na tuto informaci mě úplně prapůvodně upozornil Lukáš Bartoň) a v takovém případě bude volání této metody vždy false. Řešení je prosté, nikoliv však samospásné a to používat instanceof.

Proč ne samospásné? Protože pokud takto použijeme instanceof, pourušíme symetričnost kontraktu equals pro případ předek (A) potomek (B). Nebude totiž platit, že a.equals(b) == b.equals(a). Samozřejmě můžeme upravit equals tak, aby to prošlo. Osobně mi přijde lepší použití instanceof.

Problematika instanceof versus getClass in equals Methods celkem pěkně rozebrali Joshua Bloch a Bill Venners. Článek vřele doporučuji přečíst.

Když jsem ve třídách opravoval equals metody, tak mě trklo, kde se tam ten kód vzal. Samozřejmě jsme si jej nechali vygenerovat Eclipsem. Chtěl jsem tedy spílat na Eclipse, že negeneruje kód podle mých představ. Naštěstí jsem to neudělal, protože Eclipse při generování equals metody přímo nabízí možnost použití instanceof. Což me vede ke konstatování, že to IDE je chytřejší než opice...

Zdroje