Към съдържанието
wannacode

Поведение

Проблем

Всеки тегли по своему

Банката има клас Account със собственик и баланс. Парите се теглят на две места в програмата. Банкоматът проверява дали има достатъчно пари. Плащането с карта е писано от друг човек и не проверява:

Account account = new Account("Мария", 100);
// Банкоматът проверява дали има достатъчно пари.
if (account.balance >= 70) {
account.balance -= 70;
}
// Плащането с карта е писано от друг човек и проверка няма.
account.balance -= 50;
System.out.println(account.balance);
Изход
-20.0

Сметката е на минус. Правилото „не можеш да изтеглиш повече, отколкото имаш“ е важно, а е написано (или забравено) поотделно на всяко място, където се тегли.

Идеята

Методите живеят при данните

Логиката за теглене е част от сметката, затова я сложи в класа Account. Метод в класа, без static, е метод на обекта:

Main.java
class Account {
public String owner;
public double balance;
public Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
} else {
System.out.println("Няма достатъчно пари: " + owner);
}
}
}
public class Main {
public static void main(String[] args) {
Account account = new Account("Мария", 100);
account.withdraw(70);
account.withdraw(50);
account.deposit(20);
System.out.println(account.balance);
}
}
Изход
Няма достатъчно пари: Мария
50.0
  • Методът се вика през обект и точка: account.withdraw(70), както полетата.
  • Вътре balance е балансът на тази сметка, на обекта пред точката. Може да се пише и this.balance.
  • Проверката е написана веднъж. Който тегли през метода, минава през нея.

Account maria = new Account("Мария", 100);
Account peter = new Account("Петър", 40);
maria.withdraw(70);
peter.withdraw(70);
System.out.println(maria.balance + " " + peter.balance);
Стъпка по стъпка, с думи
  1. Две сметки, два обекта. Всеки има свой баланс.
  2. maria.withdraw(70): в this отива стрелка към обекта пред точката.
  3. В метода балансът е на this: 100 - 70 = 30.
  4. Същият метод, друг обект: сега this сочи Петър.
  5. 70 е повече от 40: методът печата отказ и балансът на Петър не се пипа.
  6. Кодът на метода е един. Полетата са на обекта, през който го викаш.

Плащането с карта вече не може да „забрави“ проверката, ако тегли с withdraw. Може обаче още да пише направо в полето. Как да го спрем, ще видим в урока „Капсулация“.

Идеята

Метод на обекта или static метод

Методите от модул „Методи“ бяха static. Те получават всичко като параметри и нямат свой обект. Методът на обекта работи с полетата на обекта, през който е извикан:

static double applyDiscount(double price, double percent) {
return price * (100 - percent) / 100;
}
double price = applyDiscount(10, 10);
account.withdraw(70);

Метод на обекта не можеш да извикаш през името на класа. Не е ясно от чия сметка да тегли:

Account.withdraw(70);
Грешка при компилиране
error: non-static method withdraw(double) cannot be referenced from a static context

Какво точно значи static и кога ти трябва, ще видим в урока „static: общото за всички“.

Пробвай

Чия сметка?

Какво ще отпечата програмата?

Account maria = new Account("Мария", 100);
Account peter = new Account("Петър", 40);
maria.deposit(30);
peter.withdraw(10);
System.out.println(maria.balance + " " + peter.balance);
Отговор

Пробвай

Намери грешката

Класът не се компилира. На кой ред е грешката?

Избери реда с грешката.

Идеята

Методът може и да отговаря

Метод на обекта може да връща резултат, както методите от модул „Методи“. Продуктът знае цената и бройката си, затова може сам да каже колко струва:

class Product {
// полетата и конструкторът, както преди
public double total() {
return price * quantity;
}
}
Product[] cart = {
new Product("Хляб", 1.20, 2),
new Product("Мляко", 2.10, 1),
new Product("Кафе", 7.50, 1)
};
double sum = 0;
for (Product p : cart) {
sum += p.total();
}
System.out.println("Общо: " + sum + " €");
Изход
Общо: 12.0 €

Сравни с първия урок от модула: там сумата се смяташе в main от полетата. Сега main само пита всеки продукт. Ако утре се добави отстъпка за количество, променяш само total().

Идеята

Как изглежда обектът като текст?

Отпечаташ ли продукт направо, излиза нещо безполезно:

Product coffee = new Product("Кафе", 7.50, 1);
System.out.println(coffee);
Изход
Product@1b6d3586

Java печата името на класа и число, което е различно при всяко пускане. По него не разбираш нищо за продукта.

Класът може да каже как да се показва. Методът се казва toString() и връща текст:

class Product {
// полетата и конструкторът, както преди
public double total() {
return price * quantity;
}
@Override
public String toString() {
return name + " x" + quantity + ": " + total() + " €";
}
}
Product coffee = new Product("Кафе", 7.50, 1);
Product bread = new Product("Хляб", 1.20, 2);
System.out.println(coffee);
System.out.println("В количката: " + bread);
Изход
Кафе x1: 7.5 €
В количката: Хляб x2: 2.4 €

Не го викаш сам. System.out.println(coffee) и "В количката: " + bread го викат вместо теб, когато трябва обектът да стане текст.

@Override пред метода засега пиши точно така. Какво значи, ще разберем в модул „Наследяване и полиморфизъм“. Името трябва да е точно toString, с малко t и главно S.

Пробвай

Книга в изречение

Какво ще отпечата програмата?

class Book {
public String title;
public String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
@Override
public String toString() {
return title + " от " + author;
}
}
Book book = new Book("Тютюн", "Димитър Димов");
System.out.println("Чета: " + book);
Отговор

Упражнение

Обекти, които правят нещо

Реши задачите в редактора си. Вземи класовете от миналите уроци и им добави методи.

  1. Person: метод introduce(), който печата името и възрастта.
  2. SimpleCar: метод accelerate(), който увеличава скоростта с 10. Сега main не пипа скоростта направо.
  3. DanceMove: метод perform(), който „изпълнява“ стъпката.
  4. Book: метод read(), който печата „Прочитане на [заглавие] от [автор]“.

Създай по един обект от всеки клас и извикай методите му:

Изход
Здравей, аз съм Мария и съм на 34 г.
Скорост след ускоряване: 10
Изпълнявам moonwalk за 5 секунди
Прочитане на Под игото от Иван Вазов

Упражнение

Кола с гориво

Клас Car с марка, скорост, гориво (в литри) и изминати километри. Конструкторът получава марката и горивото, скоростта и километрите започват от 0.

  • accelerate() увеличава скоростта с 10.
  • refuel(double liters) долива гориво, но резервоарът побира най-много 50 литра.
  • drive(int distance) харчи по 6 литра на 100 км и добавя разстоянието към километрите. Ако горивото не стига, печата съобщение и колата не тръгва.
  • toString() връща марката, километрите и горивото.
Main
Car car = new Car("Toyota", 20);
car.accelerate();
car.accelerate();
System.out.println("Скорост: " + car.speed);
car.drive(100);
System.out.println(car);
car.drive(300);
car.refuel(40);
System.out.println(car);
car.drive(300);
System.out.println(car);
Изход
Скорост: 20
Toyota: 100 км, 14.0 л
Няма достатъчно гориво за 300 км
Toyota: 100 км, 50.0 л
Toyota: 400 км, 32.0 л
Подсказка: distance * 6 / 100.0. С 100 вместо 100.0 делението е целочислено и за 30 км ще излезе 1 литър вместо 1,8.

Още задачи

  • Отстъпка. Добави към Product метод applyDiscount(double percent), който намалява цената на продукта. Сравни го със static метода от модул „Методи“, който връщаше нова цена.
  • Сметка с история. Добави към Account брояч на операциите: всяко внасяне и успешно теглене го увеличава с 1. Метод toString() връща собственика, баланса и броя на операциите.

Тест

Провери се

1. Какво прави maria.deposit(30)?
2. Какво вижда методът на обекта, което static методът не вижда?
3. Защо Account.withdraw(70); не се компилира?
4. Кога се вика toString() сам?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·