Statické proměnné a metody Java je ukecaná Úvod • Se statickou metodou jsme se setkali už u úplně prvního programu - Hello, world! public class Demo {   public static void main(String[] args) {   System.out.println("Hello World!");   } } • public reprezentuje viditelnost — metoda je veřejná • void reprezentuje návratový typ — nic nevrací • Co reprezentuje static? • Metoda static v Javě (jinde může být chápáno jinak) říká, že metoda nepotřebuje pro své fungování žádný konkrétní existující objekt, s nímž by pracovala • To přesně potřebujeme u main, neboť žádný objekt dosud nemáme 1 Dosud byly nestatické metody a atributy • Dosud jsme zmiňovali atributy (proměnné) a metody objektu. • Jméno (atribut String name) patří přímo jedné osobě • Metoda toString vrátí Person jméno této osoby public class Person {   private String name; // name of this person   ...   public String toString() {   return "Person " + name; // returns name of this person   } } Jak fungují statické metody a atributy • Lze deklarovat také metody a proměnné patřící celé třídě, tj. všem objektům třídy. • Taková proměnná (nebo metoda) existuje pro jednu třídu jen jednou. • Označujeme ji static. Použití statických metod • Chceme metodu max, která vrací maximální hodnotu dvou čísel • K tomu žádné objekty nepotřebujeme • Uděláme ji jako statickou public class Calculate {   public static int max(int a, int b) {   if (a > b) return a;   return b;   } } Jde i jako nestatická? • Ano, takto. Vynecháme static. 2 public class Calculate {   public int max(int a, int b) {   if (a > b) return a;   return b;   } } • Na spuštění max budu nyní potřebovat objekt třídy Calculate Calculate c = new Calculate(); int max = c.max(1, 2); // method needs an object `c` • Ovšem ten objekt c je tam úplně k ničemu, s žádnými jeho atributy se nepracuje a ani žádné nemá Řešení • Uděláme metodu statickou. • Pak metoda patří celé třídě a zavoláme ji názvem třídy bez konkrétního objektu public class Calculate {   public static int max(int a, int b) {   if (a > b) return a;   return b;   } } ... int m = Calculate.max(1, 2); Zpříjemnění použití statických metod • Někdy v kódu často používáme statické metody určité třídy, např. naše Calculator.max • Pro kratší zápis lze pak využít deklaraci import static dané metody • nebo všech metod přes *, např. import static cz.muni.fi.pb162.Calculator.*; ... int m = max(1, 2); 3 Typické použití statických metod • Velmi často se používají v obdobných situacích, jako výše uvedené max • Tzn. jednoduše pro implementaci funkce, která nevyužívá žádné atributy (data objektu), pouze dostane vstupy a něco vrátí • Pak ani logicky žádný objekt nepotřebuje • Příklady: metody třídy java.util.Arrays Statické proměnné (atributy třídy) • Doposud jsem měli pouze proměnné (atributy) patřící konkrétnímu objektu • Např. ve třídě Person, která reprezentuje člověka, má každý člověk své (obvykle i odlišné) jméno • Někdy je ale situace, kdy pro celou třídu stačí určitý údaj jenom jednou • Příklad: chceme jsi pamatovat, kolik lidí se nám během chodu programu vytvořilo • Jak to udělat? Počítaní lidí • Vytvoříme statickou proměnnou peopleCount a každý člověk ji při svém vzniku zvýší o jedna. public class Person {   private String name;   private static int peopleCount = 0;   public Person(String name) {   this.name = name;   peopleCount++;   }   public static int howManyPeople() {   return peopleCount;   } } • Logicky na vrácení počtu lidí stačí statická metoda howManyPeople() Počítaní lidí II • Použití bude vypadat následovně 4 Person.howManyPeople(); // 0 Person jan = new Person("Jan"); Person.howManyPeople(); // 1 Person anna = new Person("Anna"); Person.howManyPeople(); // 2  Více informací: Java tutorial — Class variables Voláni statické metody Můžeme volat statickou metodu nad konkrétním objektem (instancí) dané třídy? Person anna = new Person("Anna"); anna.howManyPeople(); • Ano, není to problém. • Přes třídu Person je to však správnější. Voláni nestatické metody • Můžeme volat nestatickou metodu jako statickou? Person.getName(); • Logicky ne! • Co by mohlo volání Person.getName() vrátit? Nedává to smysl. • Jde nám přece o jméno konkrétního člověka, tj. atribut v konkrétním objektu Person • Atribut name se nastaví až při zavolání konstruktoru Přístup ze statické metody k nestatickému atributu? • Obdobně platí pro atributy, tj. NELZE toto: 5 public class NonStaticTest {   private int count = 0;   public static void main(String args[]) {   count++; // compiler error   } } • Java ohlásí při překladu chybu: non-static variable count cannot be referenced from a static context. • Metoda main je statická — může používat pouze statické metody a proměnné. Přístup k nestatickému atributu • Jedině po vytvoření konkrétní instance: public class NonStaticTest {   private int count = 0;   public static void main(String args[]) {   NonStaticTest test = new NonStaticTest();   test.count++;   } }  Všimněte si, že ve třídě mohu vytvořit objekt té stejné třídy. • Dalším řešením by bylo udělat count statický. Problémy se statickými metodami? NE • Ano, použití static není tak prosté, jak jsme dosud prezentovali :-) • Statické metody většinou problém nejsou • Jednoduše slouží k realizaci nějaké činnosti, které stačí předané vstupy (parametry) a která nepotřebuje žádný "svůj" objekt s atributy • Důkazem je řada použití statických metod v Java Core API, kde jsou třídy, které mají jen statické metody • Takovým třídám se říká utility classes (jakési "účelové" třídy) Problémy se statickými proměnnými? ANO • U statických proměnných je to složitější • Jejich použití musí být hodně dobře zdůvodněné 6 • Opravdu potřebujeme danou hodnotu pro danou třídu právě jednou??? • Nestane se dobudoucna, že jich budeme potřebovat více??? Možné řešení — singleton • Často je lepší aplikovat návrhový vzor singleton (jedináček) • Jedná se běžnou třídu, např. PersonCounter, která má své běžné nestatické atributy a metody zajišťující potřebnou funkcionalitu ◦ Např. metody void increaseNumPeople() a atribut int peopleCount zajišťující počítání vytvořených lidí. public class PersonCounter {   private int peopleCount = 0;   public void increaseNumPeople() {   peopleCount++;   }   public int howManyPeople() {   return peopleCount;   } } Možné řešení — singleton (pokr.) • Singleton ale navíc zajišťuje vytvoření jediné sdílené instance (např. jediného počítadla lidí pro celý systém): ◦ Zakáže se volání konstruktoru (např. tak se vytvoří jediný bezparametrický privátní konstruktor) ◦ Místo volání konstruktoru se nabídne veřejná metoda getInstance(), která zjistí, jestli (jediná) instance již existuje. Pokud ne, vytvoří jí (voláním privátního konstruktoru) a uloží do statického atributu třídy. Pokud již existuje, rovnou se instance vrátí. • A dále zajišťuje jednoduchý přístup k jediné instanci odkudkoliv: ◦ Metoda getInstance() je statická, tj. kdokoliv odkudkoliv může zavolat PersonCounter.getInstance().increaseNumPersons(). ◦ Pokud chceme automaticky počítat vytvoření instance třídy Person z předchozího příkladu, lze toto volání jednoduše přidat konstruktorů třídy Person Kompletní příklad singletonu 7 public class PersonCounter {   // here will be the singleton instance (object)   private static PersonCounter counter = null;   private int peopleCount = 0;   // "private" prevents creation of instance via new PersonCounter()   private PersonCounter() {}   // creates the singleton unless it exists   public static PersonCounter getInstance() {   if(counter == null) {   counter = new PersonCounter();   }   return counter;   }   public void increaseNumPeople() {   peopleCount++;   }   public int howManyPeople() {   return peopleCount;   } } Import statických prvků • Už jsme ukázali výše, že statické třídy i metody můžeme importovat: import static java.lang.System.out; public class HelloWorld {   public static void main(String[] args) {   out.println("Hello World!");   } } • relevantní pouze pro statické metody a proměnné • vhodně použitelné pro některé věci z Java Core API, např. Math  Více informací: Wikipedia:Static import Problémy importu statických prvků 8 import static java.lang.System.out; // developer is reading the code out.println("Hello World!"); // what is out? // few lines above: PrintStream out; // ahh ok, I thought it was System.out • Takže někdy pak nevíme, jestli jde o statický import a nebo jen o lokální proměnnou/metodu. • A jakmile něco nevíme na první pohled, JE TO ŠPATNĚ! :-) == 9