final та інші ключові слова java - 1

— Привіт, Аміго!

— Привіт, Білаабо!

— Я розповім тобі сьогодні про кілька ключових слів у Java. Але почну із найцікавішого – ключового слова final. Якщо перекласти його з англійської, то вийде щось на зразок фінальний або остаточний.

Ключове слово final можна додавати при оголошенні змінної, методу та класу.

— А навіщо потрібний цей final?

— Все досить просто. Якщо ми помітили змінну словом final, вона стає незмінною:

final int i = 5;
i++; //помилка компіляції – не можна змінювати значення змінної i

— Ясно.

— Якщо ми позначили метод словом final, цей метод заборонено перевизначати в класах-спадкоємцях:

class Cat
{
 public final String getName()
 {
  return "cat";
 }
}

class Tiger extends Cat
{
 public String getName() //помилка компіляції – не можна перевизначати метод getName()
 {
  return "tiger";
 }
}

— Зрозуміло. А навіщо може знадобитися забороняти перевизначення методу?

— Наприклад, програміст написав у цьому методі багато важливого коду і хоче, щоб усі спадкоємці його класу гарантовано мали задану поведінку.

І нарешті – третє.

Якщо ми позначимо словом final клас, то таким чином ми заборонимо успадковуватись від нього.

public final class Cat
{
 public String getName()
 {
  return "cat";
 }
}

class Tiger extends Cat //помилка компіляції – не можна успадковуватися від класу Cat
{
 public String getName()
 {
  return "tiger";
 }
}

— А навіщо забороняти спадкування класів?

— Ти повинен зрозуміти, що заборона успадкування йде не зі шкідливості, а задля безпеки та цілісності коду. Якщо успадкування класу не заборонено, мається на увазі, що воно дозволене. І код проектувальника класу нормально працюватиме і з об'єктами його класу, і з об'єктами класу-спадкоємця.

А ось якщо розробник бачить, що навіть за невеликих змін у його класі, все перестане працювати, як задумано, тоді йому краще заборонити спадкування.

— Клас String, наприклад, оголошений як final, як і всі примітивні типи: Integer, Boolean, Double, Character…

— Ага, розумію. Клас String зроблений immutable і якби раптом з'явилися рядки, що змінюються, то багато чого перестало б працювати.

— Ну майже. Скажімо так, все працювало б майже по-старому, але іноді виникали помилки, які було б дуже складно знайти і зрозуміти. Тому, в деяких випадках успадкування класів або методів не гріх і заборонити – менше помилок потім виловлювати.

— А де ще можна писати final?

— final можна писати перед змінними-аргументами функції і перед змінними методом. Ось приклад:

public void setName(final String name)
{
 final String displayName = "Mr."+name;
  …
 this.name = displayName;
}

— А який у цьому сенс?

— Ну, смислів – два. По-перше, ми оголошуємо змінну final – якщо хочемо повідомити інших розробників, що це значення – певна константа, а не просто змінна.

Наприклад, ми хочемо розрахувати ПДВ від ціни:

public int getPriceNDS()
{
 final int NDS = 20;
 return this.price * NDS / 200;
}

І по-друге, якщо ми писатимемо локальні чи анонімні внутрішні класи, то такі змінні нам знадобляться. Я розповім про такі класи найближчим часом. Але не сьогодні.

— Ок, поки що начебто нічого складного.

— Зверніть увагу, що незмінною стає тільки змінна, а не об'єкт, на який вона посилається. Об'єкт можна міняти ще як.

— Саме хотів уточнити цей момент. А чи немає способу зробити об'єкт незмінним?

— Ні, тільки якщо ти напишеш immutable клас.

Зверніть увагу на такий момент — т.к. значення змінної міняти не можна, їй відразу потрібно привласнити початкове значення:

Цей код скомпілюється Цей код не скомпілюється
class Home
{
 private final int width = 200;
 private final int height = 100;

 public Home()
 {
 }
}
class Home
{
 private final int width;
 private final int height;

 public Home()
 {
 }
}

Але, водночас, Java дозволяє перенести ініціалізацію final-змінних класу в конструктор.

Цей код скомпілюється Цей код не скомпілюється
class Home
{
 private final int width = 200;
 private final int height;

 public Home()
 {
  height = 100;
 }
}
class Home
{
 private final int width;
 private final int height;

 public Home()
 {
  height = 100;
 }
}

Більш того, у різних конструкторах final-змінні можна ініціалізувати різними значеннями. Це дуже зручно:

Цей код скомпілюється
class Home
{
 private final int width;
 private final int height;

 public Home()
 {
  height = 100;
  width = 200; 
 }

 Public Home(int width)
 {
this.height = 300;
  this.width = width;
 }

 Public Home(int width, int height)
 {
this.height = height;
  this.width = width;
 }
}

— Справді цікава тема, і абсолютно все зрозуміло, дякую, Білаабо!