Seminář 5 KMI/ZP3JV Základy programování 3 (Java)

Vnořené třídy

Materiály

Vnořené třídy

Jazyk Java umožňuje definovat vnořené třídy (statické a nestatické). Toho se využívá zejména v situacích, kdy je třída použita pouze na jednom jediném místě. Pro zpřístupnění vnitřní třídy je nutné se nejprve odkázat na vnější třídu:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

Lokální třídy

Jsou definovány uvnitř bloku. V zásadě jsou podobné vnořeným třídám. Podrobnější informace naleznete zde

Anonymní třídy

Umožňují vytvořit instanci třídy přímo při její deklaraci. Chovají se jako lokální třídy, ale nemají jméno. Používají se zejména pokud je zapotřebí využít lokální třídu pouze jednou.

HelloWorld frenchGreeting = new HelloWorld() {
  String name = "tout le monde";
  public void greet() {
    greetSomeone("tout le monde");
  }
};

Shadowing

Deklarace vnořených tříd v některých případech mohou překrýt vlastnosti nadřazené třídy. Nelze se tedy na ně odkazovat pouze jejich jménem.

public class ShadowTest {
  public int x = 0;

  class FirstLevel {
  public int x = 1;

  void methodInFirstLevel(int x) {
    System.out.println("x = " + x);
    System.out.println("this.x = " + this.x);
    System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
    }
  }

  public static void main(String... args) {
    ShadowTest st = new ShadowTest();
    ShadowTest.FirstLevel fl = st.new FirstLevel();
    fl.methodInFirstLevel(23);
    // vystup: x = 23, this.x = 1, ShadowTest.this.x = 0
  }
}

Lambda výrazy

Při použití anonymních tříd v případě, že implementují jednoduchou funkcionalitu je samotná syntaxe značně složitá a nepřehledná.

(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }
() -> { return 3.1415 };

Spustitelné Lambda výrazy

Runnable r1 = new Runnable() {
  @Override
  public void run() {
    System.out.println("Hello world one!");
  }
};

// Lambda Runnable
Runnable r2 = () -> System.out.println("Hello world two!");
r1.run();
r2.run();

Runnable představuje funkční interface. V Java 8 jich nalezneme celou řadu. Navíc lze definovat vlastní.

Funkční interface

@FunctionalInterface
public interface WorkerInterface {
  public void doSomeWork(); 
}

Může mít jen jednu metodu.

// Starý způsob
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}
 
// Pomocí lambda výrazu:
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
 
// Syntaktický cukr v Java 8
list.forEach(System.out::println);

Úkoly

Soubory neformátované dle konvencí budou vráceny k přepracování!

  1. S využitím rozhraní

    public interface Mapping {
      Object map(Object o);
    }
    
    implementujte metodu List<Object> map(List<Object> list, Mapping m), která se chová stejně jako funkce map, známá z jazyka Scheme/Lisp.

  2. Stejným způsobem navrhněte metodu List<Object> filter(List<Object> list, Condition c), která ze seznamu vybere hodnoty splňující danou podmínku. Navrhňete vhodnou třídu/rozhraní Condition. Metodu otestujte výběrem sudých celých čísel ze seznamu. Podmínku vytvořte jako vnitřní třídu, jako anonymní třídu i jako lambda výraz.

  3. Implementujte statickou metodu String repeatChar1(char c, int n), která vytvoří řetezec skládající se z n znaků c s použitím konkatenace řetězců (tj. s += c). Implementujte metodu String repeatChar2(char c, int n), která se bude chovat identicky jako metoda repeatChar1, ale bude používat StringBuilder. Změřte rychlost obou metod (s využitím System.currentTimeMillis) při vytváření řetězce o velikosti 100000 znaků.

© Martin Trnečka