Zapouzdření

Tomáš Pitner, upravil Marek Šabo
tomp@fi.muni.cz

Co je zapouzdření

  • Naprosto zásadní vlastnost objektového přístupu, možná nejzásadnější
  • Jde o spojení dat a práce s nimi do jednoho celku - objektu
  • Data jsou v atributech objektu, práce je umožněna díky metodám objektu
  • Data by měla být zvenčí (jinými objekty) přístupná jen prostřednictvím metod
  • Data jsou tedy "skryta" uvnitř, zapoudřena
  • To zajistí větší bezpečnost a robustnost, přístup k datům máme pod kontrolou

Motivace zapouzdření

class Person {
   String name;
   int age;

   Person(String inputName, int inputAge) {
     name = inputName;
     age = inputAge;
   }
 }

Co když:

  • Nechceme, aby kdokoli mohl modifikovat atributy name, age po vytvoření objektu
  • Většinou ale chceme, aby konstruktor a třídu mohl používat každý

Řešení — práva přístupu

  • Nastavíme práva přístupu pomocí modifikátorů třídy, metody nebo atributu
  • Nechceme modifikovat atributy name, age po vytvoření objektu? ⇒ použijeme klíčové slovo private
  • Chceme, aby mohl konstruktor a třídu používat skutečně každý? ⇒ použijeme klíčové slovo public
public class Person {
  private String name;
  private int age;

  public Person(String inputName, int inputAge) {
    name = inputName;
    age = inputAge;
  }
}

4 typy viditelnosti v zkratce

  • public = veřejný, může používat každý i mimo balík ⇒ používejte na třídy a (některé) metody
  • private = soukromý, nemůže používat nikdo mimo třídy ⇒ používejte na atributy
  • protected = chráněný ⇒ používá se při dědičnosti, vysvětlíme později
  • modifikátor neuveden, pak jde o možnost přístupu "package local"

    • v rámci balíku se chová jako public, mimo něj jako private
    • v našem kurzu tento typ nebudeme používat

  • Ujistěte se, že vždy máte zadefinovaný práva přístupu/typ viditelnosti.
  • V drtivé většině budete používat public a private.

Metody get & set — motivace

public class Person {
   private String name;
   private int age;

   public Person(String inputName, int inputAge) {
     name = inputName;
     age = inputAge;
   }
 }
  • Klíčové slovo public umožňuje použít třídu Person všude

    Person p = new Person("Marek", 23); // even from another class/package

  • Klíčové slovo private zabraňuje získat hodnotu atributů p.name, p.age.

Metody get

  • Chci získat hodnotu atributu i po vytvoření objektu,
  • ale zabránit jeho modifikaci?
  • Do třídy přidáme metodu, která bude veřejná a po zavolaní vrátí hodnotu atributu.
public int getAge() {
  return age;
}
  • Takové metody se slangově nazývají "gettery".
  • Mají návratovou hodnotu podle typu vraceného atributu.
  • Název metody je vždy get + jméno atributu s velkým písmenem (getAge, getName, …​).

Metody set

  • Chci-li nastavit hodnotu atributu i po vytvoření objektu:
public void setAge(int updatedAge) {
  age = updatedAge;
}
  • Metoda je veřejná a po jejím zavolaní přenastaví původní hodnotu atributu.
  • Takové metody se slangově nazývají settery.
  • Mají návratovou hodnotu typu void (nevrací nic).
  • Název metody je vždy set + jméno atributu s velkým písmenem (setAge, setName, …​).

Příklad atribut a get & set

public class Person {
  private String name; // attribute
  public String getName() { // its getter
    return name;
  }
  public void setName(String newName) { // its setter
    name = newName;
  }
}

Viditelnost atributů

  • Není lepší udělat atribut public, namísto vytváření metod get a set?
  • Není, neumíme pak řešit tyhle problémy:
  • Co když chci jenom získat hodnotu atributu, ale zakázat modifikaci (mimo třídy)? ⇒ Řešení: odstraním metodu set
  • Chci nastavit atribut věk (v třídě Person) pouze na kladné číslo? ⇒ Řešení: upravím metodu set:

    • if (updatedAge > 0) age = updatedAge;

  • Chci přidat kód provedený při získávání/nastavování hodnoty atributů? ⇒ Řešení: upravím metodu get/set
  • Gettery & settery se dají ve vývojových prostředích (NetBeans, IDEA) generovat automaticky.

Využití this

public void setAge(int updatedAge) {
  age = updatedAge;
}
  • Mohli bychom nahradit jméno parametru updatedAge za age?
  • Ano, ale jak bychom se potom dostali k atributu objektu?
  • Použitím klíčového slova this:
public void setAge(int age) {
  this.age = age;
}
  • this určuje, že jde o atribut objektu, nikoli parametr (lokální proměnnou)

Korektní použití třídy I

public class Person {
   private String name;
   public Person(String name) {
     this.name = name;
   }
   public void writeInfo() {
     System.out.println("Person " + name);
   }
   public String getName() {
     return this.name;
   }
}

Korektní použití třídy II

  • Vytvoříme dvě instance (konkrétní objekty) typu Person.
public class Demo {
   public static void main(String[] args) {
      Person ales = new Person("Ales");
      Person beata = new Person("Beata");
      ales.writeInfo();
      beata.writeInfo();
      String alesName = ales.getName(); // getter is used
      // String alesName = ales.name; // forbidden
   }
}

Třída Account — připomenutí

public class Account {
  private double balance;
  public void add(double amount) {
    balance += amount;
  }
  public void writeBalance() {
    System.out.println(balance);
  }
  public void transferTo(Account whereTo, double amount) {
    balance -= amount; // change the balance
    whereTo.add(amount);
  }
}

Třída Account — řešení

public void transferTo(Account whereTo, double amount) {
  this.add(-amount);
  whereTo.add(amount);
}

/