Statické metódy a ich úskalia v Jave

28. dubna 2009

V nasledovnom článku sa bližšie pozrieme na prekrývanie statických metód a na dôsledky, ktoré z toho vyplývajú. Nie tak dávno som bol pred problémom refaktoringu DB objektov. Aplikácia stará viac ako 7 rokov bola vyvíjaná ľuďmi čo sa na tom učili. Nikoho isto neprekvapí, že z času na čas je v takýchto aplikáciách potrebné urobiť trošku prievan a zrefaktorovať kód, pretože pokračovanie znamená len kopenie ďalších problémov.

Cieľom bolo znížiť duplicitu kódu, oddeliť kód špecifický pre objekty, od zdieľaného kódu, vyčlenenie kódu do statických metód a tak ďalej. Pri poslednom menovanom som narazil na niektoré úskalia pri definovaní a využívaní statických metód. O tých by som rád napísal v nasledovnom príspevku.

Možnosti statických metód

Statické metódy najčastejšie využívame ako pomocné metódy, ktoré všetky potrebné parametre dostanú na vstupe a po spracovaní vrátia výsledok. Väčšinou sú to rôzne formátovacie a kontrolné metódy. Tie sa často spájajú a vytvárajú tak pomocné triedy.

Dajú sa statické metódy prepísať?

Nie, avšak sa dajú prekryť. Ich funkčnosť potom závisí od toho ako sú volané neskôr.

Použitie

Definujme si nasledovné triedy:

Abst.java

abstract public class Abst {
  public static String getName() {
    return „Abstraktna trieda“;
  }
  public String getText() {
    return getName();
  }
}

A.java

public class A extends Abst{
  public static String getName() {
    return „A“;
  }
}

B.java

public class B extends A{
  public static String getName() {
    return „B“;
  }
}

C.java

public class C extends B{
  public static String getName() {
    return „C“;
  }
}

Vytvorili sme triedy, ktoré vždy prekrývajú telo statickej metódy svojho predka. Prekrývanie statických metód sa neodporúča, nakoľko ich chovanie nemusí vždy byť vždy také ako by sme očakávali od prepisovaných metód. Osobne som názoru, že ich použitie vytvára zmätok v kóde a poukazuje na zlý návrh.

Napríklad Eclipse ani nedovolí vygenerovať taký kód, kedže sa nejedná o prepisovanie, ale len o prekrytie. Takže tuto obludnosť musíme napísať sami. Ešte by som dodal, že nad takto definovanou statickou metódou nie je možné použiť anotáciu „@Override“.

Pre Eclipse užívateľov

Volaním Eclipse funkcie „Quick Type Hierarchy“ (pri štandardnom nastavení klávesnice, CTRL-T) sa hierarchicky zobrazuje, v ktorých triedach potomkov a predkov je métodá prepisovaná, v prípade statickej metódy prekrytá.

Testovanie

Pri priamom volaní našich statických metód funguje všetko ako by sme čakali.

Abst.getName() : Abstraktna trieda
A.getName() : A
C.getName() : C

Ako ovplyvní výsledok volanie z inštancii tried?

Abst a = new C();
a.getName(): Abstraktna trieda
a.getText(): Abstraktna trieda

Toto je štandardné očakávanie. Nikto nepredpokladá, že niekto neskôr prekrýva statickú metódu definovanú v abstraktnej triede. Avšak my prekrývame a pri zmene bázovej triedy následne dostaneme iný výsledok.

B b = new C();
b.getName(): B
b.getText(): Abstraktna trieda

Vidíme, že volanie statickej metódy volalo metódu bázovej triedy, v tomto prípade triedy B. Ďalšia záludnosť spočíva v prípade modifikácie triedy A prepísaním metódy getText() nasledovným kódom:

  @Override
  public String getText() {
    return getName();
  }

kde dostaneme pri volaní vyššie uvedených príkladov nasledovné výsledky

Abst a = new C();
a.getName(): Abstraktna trieda
a.getText(): A
B b = new C();
b.getName(): B
b.getText(): A

Dá sa vždy zavolať tá „správna“ prekrytá metóda?

Áno, dá. Avšak tú správnu statickú metódu je potrebné zavolať pomocou reflexie.

Do abstraktnej triedy Abst pridáme nasledovný kód:

  public String getNameDynamic() {
    try {
      return (String) this.getClass().getMethod(„getName“).invoke(this);
    } catch (Exception e) {
      // v normalnom kode RuntimeException nepouzivat!
      // toto je len pre testovacie ucely
      throw new RuntimeException(e);
    }
  }

Vďaka tomu sa vždy zavolá správna statická metóda, nezávislá od bázovej triedy.

A výsledok:

Abst a = new C();
B b = new C();
a.getNameDynamic(): C
b.getNameDynamic(): C

Záver

Na týchto príkladoch som chcel poukázať na úskalia pri prekrývaní statických metód. Ako som už písal vyššie, samotné prekrývanie statických metód u tried potomkov považujem za zlý prístup a zlý návrh samotného programu. Takéto riešenie problému by nemalo byť použité vôbec.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *