Seminář 2

Table of Contents

Materiály

Třídy

Každá veřejná třída je umístěna v samostatném souboru, který nese stejné jméno jako třída, tj. třída Foo, je umístěna v souboru Foo.java. Název třídy začíná vždy velkým písmenem. V názvech se nepoužívají "_" (podtržítka).

Jednotlivé třídy jsou umísťovány do balíčků, které představují:

  1. cesty ve struktuře adresářů, např. cz/upol/zp3jv/lecture02/Foo.java
  2. jmenné prostory oddělující jednotlivé třídy
    • plný název třídy je pak cz.upol.zp3jv.lecture02.Foo
    • dvě třídy stejného jména mohou být umístěny v různých jmenných prostorech, např. cz.upol.zp3jv.lecture02.Foo a edu.mit.Foo jsou odlišné třídy

Deklarace a struktura třídy

public class Foo {      
  // deklarace atributu (clenskych promennych) 

  // konstruktory

  // metody
}

Atributy

<viditelnost> <modifikátory> <typ> <jméno> [ = inicializační hodnota ];

Viditelnost:

  • public - atribut je přístupný z jakékoliv třídy
  • private - atribut je přístupný pouze ze třídy, ve které je deklarovan
  • protected - atribut je přístupný ze třídy, kde je deklarovan, jejich potomků a ze tříd, které se nachází ve stejném balíčku
  • pokud není viditelnost uvedena, je atribut přístupný ze třídy, kde je deklarován nebo ze tříd, které se nachází ve stejném balíčku

Modifikátory:

  • static - atribut deklarovaný jako static je chápan jako atribut třídy (je sdílená všemi instancemi dané třídy)
  • final - atribut se nebude měnit (konstanta)

Jméno:

  • názvy atributů začínají vždy malým písmenem a v názvech se nepoužívá podtržítko (např. age, numberOfLegs)
  • názvy konstant se píšou vždy velkými písmeny s podtržítky (např. SPEED_OF_LIGHT)
private int foo;
public double answerToUltimateQuestionOfLife = 42.0;
public static final int SPEED_OF_LIGHT = 299792458;

Poznámka:

  • Každá třída obsahuje dva ,,implictní atributy'' this a super odkazující na aktuální objekt a na objekt předka.
  • Ve většině případů se atributy deklarují jako private a přistupuje se k nim pomocí vyčleněnných metod, tzv. getterů a setterů.

Metody

<viditelnost> <modifikátory> <návratový-typ> <jméno-metody>([typ-1 argument-1, [ typ-2 argument-2, ... [ typ-N argument-N ]]]) {
  // tělo metody
}

Viditelnost:

  • Stejná klíčová slova a význam jako v případě atributů.

Modifikátory:

  • static - jedná se o metodu třídy (metoda je přístupná bez vytvoření instance dané třídy)
  • final - metodu nepůjde překrýt v potomkovi
  • abstract - jedná se o abstraktní metodu, která nemá tělo; pokud třída obsahuje takovou metodu, je nutné samotnou třídu deklarovat jako abstraktní, tzn. public abstract class Foo.

Poznámka: Statické metody by měly být používány pouze v opodstatněných případech. Tedy pouze tehdy pokud má smysl volat danou metodu, bez existující instance!

private int add(int a, int b) {
  return a + b;
}

public void printMsg(String msg, int count) {
  for (int i = 0; i < count; i++) {
    System.out.println(msg);
  }
}

Konstruktory

<viditelnost> NazevTridy([typ-1 argument-1, [ typ-2 argument-2, ... [ typ-N arugment-N ]]]) {
  // zavolani rodicovskeho konstruktoru
  super(...); 

  // tělo konstruktoru
  // typicky inicializace proměnných
}	

Poznámka: Třída může mít více metod a konstruktorů stejného jména. V takovém případě se rozhodne o tom, která metoda/konstruktor bude zavolána na základě předaných typů argumentů.

public void print(int number) {
  System.out.println("Int: " + number);
}

public void print(String str) {
  System.out.println("String: " + str);
}

Použití tříd a objektů v programu

Vytvoření nové instance třídy se provádí pomocí operátoru new. Např.:

Foo foo = new Foo();

K metodám a proměnným dané třídy se přistupuje pomocí operátoru . Např.:

foo.bar = 42;
foo.baz(42);

Příklad třídy, představující nějaké obecné zvíře:

public class Animal {

  private int legs;
  private int age;

  public Animal(int legs, int age) {
    super();
    this.legs = legs;
    this.age = age;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public int getLegs() {
    return legs;
  }
}

public class Example1 {
  public static main(String[] args) {
    Animal dog = new Animal(4, 6);
    Animal penguin = new Animal(2, 1);

    System.out.println(dog.getLegs() + penguin.getLegs());
  }
}

Dědičnost

V Javě může třída dědit pouze z jednoho předka. Z pohledu dědičnosti všechny třídy tvoří souvislou hierarchii, kde na vrcholu je třída Object. Dědění se deklaruje pomocí klíčového slova extends a názvu třídy, např.:

public class Bar extends Foo { /*...*/ }.

public class Dog extends Animal {

  public static final int OLD_DOG = 10;

  // predpokladame, ze kazdy pes ma ctyri nohy
  public Dog(int age) {
    super(4, age);
  }

  // nove pridana metoda
  public void fetch() {
    if (getAge() < OLD_DOG) System.out.println("Your dog is fetching a stick");
    else  System.out.println("Your dog is ignoring you.");
  }
}

public class Example2 {
  public static main(String[] args) {
    Dog dog = new Dog(6);
    Animal penguin = new Animal(2, 1);

    System.out.println(dog.getLegs() + penguin.getLegs());
  }
}

Rozhraní

Absence vícenásobné dědičnosti je řešena pomocí tzv. rozhraní, "tříd", jejichž všechny metody jsou abstraktní. Každá třída pak může implementovat libovolné množství těchto rozhranní. Jednotlivá rozhranní se deklarují podobně jako třídy, s tím rozdílem, že místo klíčového slova class je použito klíčové slovo interface a jednotlivé metody nejsou deklarovány jako abstract. Např.

public interface Foo {
  public void bar();
  public int baz(int a, int b);
}  

Implementace daného rozhraní se deklaruje pomocí kličového slova implements.

public class Bar implements Foo { /* ... */ } Příklad:
public interface Noisy { public void makeSound(); }

public class BigDog extends Dog implements Noisy {

  public BigDog(int age) {
    super(age);
  }

  public void makeSound() {
    System.out.println("Haf Haf\n");
  }
}

public class Duck extends Animal implements Noisy {

  public Duck(int age) {
    super(2, age);
  }

  public void makeSound() {
    System.out.println("Ga Ga\n");
  }
}  

Poznámka: Díky polymorfismu můžeme ke každému objektu přistupovat, jako by se jednalo o některého z jeho rodičů, potažmo by se jednalo o objekt implementující dané rozhranní.

BigDog bigDog = new BigDog(10);
Duck duck = new Duck(3);

bigDog.fetch();
bigDog.makeSound();
duck.makeSound();

Noisy noisyAnimal = new BigDog(10);
noisyAnimal.makeSound();

Ověření, že daný objekt je instance nějaké třídy nebo že implementuje nějaké rozhranní jde pomocí operátoru instanceof. Často se používá v kombinaci s přetypováním.

if (noisyAnimal instanceof Dog) 
  ((Dog) noisyAnimal).fetch();

Proměnný počet argumentů

public static int max(int first, int... rest) {

}

Kompilátorem převedeny na public static int max(int first, int[] rest) {}. V praxi je tedy volání max(1,2,3) přeloženo na max(1, new int[] {2, 3}).

Rozhraní v Javě 8

Přestože rozhraní má představovat pouze ,,kontrakt'', jaké metody daná implementace nabízí a nemělo by obsahovat žádné implementované metody, od Javy 8 je možné v rozhraní implementovat metody označené jako static a metody označené jako default. Metody označené jako default představují implicitní implementaci dané metody a mohou být v implementujících třídách překryty. Tyto metody narušují základní myšlenku rozhraní, ale byly zavedeny proto, aby mohlo dojít k rozšíření existujících rozhraní bez narušení kompatibility s existujícími implementacemi.

Author: Martin Trnečka

Created: 2020-09-22 Tue 14:53

Validate