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;
 }
}

— Действительно интересная тема, и абсолютно все понятно, спасибо, Билаабо!