Statické proměnné a metody Tomáš Pitner, Radek Ošlejšek, Marek Šabo 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 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 1 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. 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 2 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); 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 3 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ě 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? 4 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: 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: 5 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é • 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 • Velmi často je daleko lepší aplikovat návrhový vzor singleton (jedináček) • Tj. vyrobit si třídu, která bude soustřeďovat (jako nestatické atributy) ta data, která jsme nechtěli dávat do ostatních tříd • Např. náš výše uvedený počet osob, kromě toho i další údaje, např. nějaký další souhrn za všechny osoby, třeba proměrný plat, věk… • U singletonu musíme zajistit, aby objekt existoval nejvýše jedenkrát, a to jakmile ho potřebujeme. • Musíme tedy zabránit přímému vytváření objektů zvnějšku pomocí new. • Na získání instance zvnějšku se pak použije volání statické metody, obvykle něco jako 6 getInstance(). Příklad singletonu 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 addPerson() {   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ů 7 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Ě! :-) 8