Statické metódy a premenné

<< Výnimky (vyhadzovanie, vlastné výnimky) | Obsah | >>

Dôvodom, prečo to vieme písať takto je to, že metóda sin() je statická. Jej hlavička vyzerá nasledovne:

public static double sin(double a)

Všimnime si, že metóda je označená za statickú pomocou modifikátora static.

Na rozdiel od bežných metód, statické metódy nepotrebujú inštanciu triedy (teda objekt) na to, aby sme ich mohli zavolať. Vieme ich volať priamo na triede. Volajú sa preto aj metódy triedy.

Statické metódy síce narušujú základnú filozofiu OOP, v ktorej máme objekt, na ktorom voláme metódy, ale umožňujú prehľadnejším spôsobom volať pomocné metódy, pri ktorých nie je inštancia objektu potrebná.

Statické premenné

Popri statických metódach môže mať trieda aj statické inštančné premenné. Na rozdiel od klasických inštančných premenných, ktoré si udržiava každý objekt nezávisle, hodnota statickej inštančnej premennej je zdieľaná všetkými objektami danej triedy. Statickú inštančnú premennú možno chápať ako ,,kolektívnu pamäť" všetkých objektov daného typu. Ak hodnotu statickej premennej zmeníme v jednom objekte, zmena sa prejaví vo všetkých ostatných objektoch tejto triedy v rámci bežiaceho programu.

Majme napríklad statickú premennú polomerDvd v triede FilmNaDvd.

public class FilmNaDvd {
   static double polomerDvd;
   ...
}

Demonštrujme zmeny v nejakej testovanej triede:

public class TesterStatic {
   public static void main(String[] args) {
        FilmNaDvd matrix = new FilmNaDvd();
        matrix.polomerDvd = 5.5;
        FilmNaDvd shawshank = new FilmNaDvd();
        System.out.println(matrix.polomerDvd);
        System.out.println(shawshank.polomerDvd);
        shawshank.polomerDvd = 3.9;
        System.out.println(matrix.polomerDvd);
        System.out.println(shawshank.polomerDvd);
   }
}

Po spustení dostávame výpis:

5.5
5.5
3.9
3.9

Pokiaľ niekto zabudne, že ide o statickú premennú, toto chovanie môže byť mätúce. Aby sme predišli zmätku ničnetušiaceho čitateľa, je vhodnejšie namiesto zápisu matrix.polomerDvd = 5.5; použiť FilmNaDvd.polomerDvd = 5.5;. Takto je jasnejšie, že ide o inštančnú premennú spravovanú v triede a nie v jej objektoch. Samozrejme každý objekt má prístup ku všetkým statickým inštančným premenným.

Použitie statických inštančných premenných

Použitie statických inštančných premenných je nutné dobre zvážiť, pretože obvykle vedie ich použitie k zlému návrhu a mnohým ďalším problémom. V praxi existuje len niekoľko konkrétnych situácií, keď majú statické premenné svoj zmysel. V začiatkoch programovania v Jave možno povedať, že v jednoduchých programoch sa možno úplne vyvarovať statických inštančných premenných.

Konštanty

Jedno zo zmysluplných využití statických premenných spočíva v deklarovaní konštánt, teda logicky označených hodnôt, ktoré sa počas behu programu nemenia. Typíckými príkladmi sú Math.PI a Math.E alebo farby v triede Color. My si v našom príklade môžeme zmeniť polomerDvd na konštantu, keďže zrejme všetky DVD budú mať rovnaký polomer. Ak by to tak nebolo, treba opustiť statický charakter tejto premennej a urobiť z nej obyčajnú inštančnú premennú.

Konštantu v Jave označujeme modifikátorom final, ktorý hovorí, že do danej premennej možno priradiť obsah len raz. Pri ďalšom pokuse nám to už Eclipse nedovolí.

public class FilmNaDvd {
   public static final double POLOMER_DVD = 6.0;
   ...
}

Je dobrým zvykom uvádzať konštanty veľkými písmenami. Ak ide o viacslovné pomenovanie, slová oddeľujeme podtržníkom.

Statické metódy a statické premenné

Statické metódy nevedia pristupovať k inštančným premennými svojej triedy. Je to logické -- keďže statické metódy vieme volať priamo na triedach (teda bez existencie objektov), nemôžeme pracovať s hodnotami, ktoré patria konkrétnym objektom. Pri pokuse o prístup k nestatickej inštančnej premennej zo statickej metódy nám to Eclipse nedovolí:

public class FilmNaDvd {
        private double String nazovFilmu;
        static void vypisNazov()
                System.out.println(nazovFilmu); //Cannot make a static reference to a nonstatic field nazovFilmu
        }
}

Mnoho začínajúcich programátorov by sa pokúsilo vyriešiť tento problém zmenou inštančnej premennej nazovFilmu na statickú. (Ide o prvoplánové riešenie "daj pokoj, nepodčiarkuj, toto musí bežať. Keď chceš to mať statické tak si to maj.") Toto však vedie nielen k hroznému návrhu, ale zavádza do programu chyby. FilmNaDvd so statickou inštančnou premennou nazovFilmu by bol chybný: hodnota tejto premennej by sa zdieľala medzi všetkými objektami typu FilmNaDvd, čo by znamenalo, že všetky filmy na DVD by mali rovnaký názov.

Zmysluplné použitie statických metód je v pseudotriedach, ktoré sú iba akási zbierka užitočných metód a konštánt.

package sk.upjs.ics.znalosti.registracia;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public abstract class Utils {
        public static final String ODDELOVAC = ";";

        /**
         * Rozseka retazec na polozky podla oddelovaca.
         * <p>
         * "AHOJ;SVET" => ["AHOJ", "SVET"]       *
         * @param retazec vstupny retazec
         * @return zoznam jednotlivych poloziek po rozdeleni
         */

        public static List<String> explode(String retazec) {
                if (retazec == null) {
                        throw new IllegalArgumentException("Retazec nemoze byt null!");
                }
                String[] zlozky = retazec.split(ODDELOVAC);
                return Arrays.asList(zlozky);
        }

        /**
         * Zluci kolekciu poloziek a medzi kazde dve polozky vlozi oddelovac.
         * <p>
         * ["AHOJ", "SVET"]     => "AHOJ;SVET"
         * @param retazec vstupny retazec
         * @return vysledny zluceny retazec.
         */


        public static String implode(Collection<String> polozky) {
                StringBuilder stringBuilder = new StringBuilder();
                for (String polozka : polozky) {
                        stringBuilder.append(polozka);
                        stringBuilder.append(ODDELOVAC);
                }
                if (stringBuilder.length() > 0) {
                        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
                }
                return stringBuilder.toString();
        }
}

Doteraz sme používali najmä statické metódy z tried java.util.Collections, java.util.Arrays, java.lang.Math alebo java.lang.System.