Геттеры и сеттеры

🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!

Здравствуйте! Из предыдущих статей вы узнали, как объявлять полноценные классы с методами и полями. В сегодняшней статье речь пойдет о геттерах и сеттерах в Java. Это серьезный шаг вперед, вы молодцы! Но теперь я должен открыть вам неприятную правду. Мы неправильно объявили наши классы! Почему?

На первый взгляд, вот этот класс выглядит вполне нормально:

public class Cat {

   public String name;
   public int age;
   public int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}
Run Code

Но на самом деле ошибка в нем есть. Представьте: вы сидите на работе и пишете этот класс Cat, чтобы описывать кошек. Потом идете домой.

Пока вас нет, приходит другой программист. Он создает свой класс Main и начинает использовать ваш Cat:

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";
       cat.age = -1000;
       cat.weight = 0;
   }
}
Run Code

Неважно, почему он это сделал и как это произошло (может, парень устал или не выспался).

Важно другое: сейчас наш класс Cat позволяет присваивать полям абсолютно безумные значения. В результате в программе появляются объекты с неправильным состоянием – например, кот, которому -1000 лет.

Так в чем же ошибка при объявлении класса?

Мы раскрыли данные нашего объекта.

Поля name, age и weight объявлены как public. Это значит, к ним можно получить доступ из любой части программы: достаточно создать объект Cat, и любой программист сможет напрямую менять его данные через оператор точки:

Cat cat = new Cat();
cat.name = "";
Run Code

Здесь мы напрямую обратились к полю name и присвоили ему новое значение.

Нам нужно как-то защитить данные от неправильных изменений извне.

Что для этого требуется?

Во-первых, все переменные экземпляра (поля) должны быть объявлены с модификатором доступа private. Это самый строгий модификатор в Java. После этого поля класса Cat нельзя будет изменить напрямую из других классов

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";// ошибка! Поле name класса Cat — приватное!
   }
}
Run Code

Когда мы попробуем запустить этот код, компилятор тут же выдаст ошибку.

Теперь наши поля вроде бы защищены. Но тут появляется другая проблема: мы перекрыли доступ слишком жестко. Теперь даже если очень нужно, мы не сможем получить, скажем, вес какого-то кота.

А это тоже нехорошо. В таком виде наш класс получается почти бесполезным.

Что нам нужно на самом деле? Какой-то ограниченный доступ:

  • Другие программисты должны иметь возможность создавать объекты Cat
  • Они должны иметь возможность получать данные о созданных объектах — например, узнавать имя или возраст кота.
  • Должна быть возможность устанавливать значения полям, но только при соблюдении логики: нужно не допустить, чтобы в программе появлялись странные данные вроде возраста = -1000.

Вот такой приличный список требований!

На самом деле все это решается очень просто с помощью специальных методов, которые называются геттерами и сеттерами.

Их названия происходят от английских слов:

  • get — метод для получения значения поля,
  • set — метод для установки значения поля.

Давай посмотрим, как это выглядит в нашем классе Cat:

public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public int getWeight() {
       return weight;
   }

   public void setWeight(int weight) {
       this.weight = weight;
   }
}
Run Code

Как видите, все довольно просто 🙂
Названия геттеров и сеттеров обычно состоят из слов get или set плюс имя соответствующего поля.

Например, метод getWeight() возвращает значение поля weight для объекта, на котором он был вызван.

Вот как это работает в программе:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       String smudgeName = smudge.getName();
       int smudgeAge = smudge.getAge();
       int smudgeWeight = smudge.getWeight();

       System.out.println("Cat's name: " + smudgeName);
       System.out.println("Cat's age: " + smudgeAge);
       System.out.println("Cat's weight: " + smudgeWeight);
   }
}
Run Code

Консольный вывод:

Cat's name: Smudge
Cat's age: 5
Cat's weight: 4

Теперь другой класс (Main) может получать доступ к данным Cat, но только через геттеры.
Обратите внимание: у геттеров стоит модификатор доступа public, так что они доступны из любой части программы.

А как быть с изменением данных? Для этого как раз и нужны сеттеры.

Пример сеттера:

public void setName(String name) {
   this.name = name;
}
Run Code

Как видите, сеттеры тоже очень простые. Мы вызываем метод setName() у объекта Cat, передаем строку в качестве аргумента – и эта строка сохраняется в поле name объекта.

Пример использования:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);

       System.out.println("Cat's original name: " + smudge.getName());
       smudge.setName("Mr. Smudge");
       System.out.println("Cat's new name: " + smudge.getName());
   }
}
Run Code

В этом примере мы и получаем, и изменяем данные объекта. Сначала с помощью геттера выводим оригинальное имя кота. Потом с помощью сеттера меняем имя на "Mr. Smudge". И наконец, снова через геттер убеждаемся, что имя действительно изменилось.

Консольный вывод:

Cat's original name: Smudge
Cat's new name: Mr. Smudge

Так в чем же тогда разница? Ведь даже с сеттерами мы все еще можем присваивать полям недопустимые значения:

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Run Code

Консольный вывод:

Smudge's age: -1000 years

Разница в том, что сеттер – это полноценный метод. И в отличие от поля, в методе можно написать логику проверки, которая предотвратит установку неправильных данных. Например, вы можете легко предотвратить присвоение отрицательного числа в качестве возраста:

public void setAge(int age) {
   if (age >= 0) {
       this.age = age;
   } else {
       System.out.println("Error! Age can't be negative!");
   }
}
Run Code

И теперь наш код работает правильно!

public class Main {

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5, 4);
       smudge.setAge(-1000);

       System.out.println("Smudge's age: " + smudge.getAge());
   }
}
Run Code

Консольный вывод:

Error! Age can't be negative!
Smudge's age: 5 years

Внутри сеттера мы создали ограничение, которое защищает нас от попытки установить недопустимые данные. Как видно, возраст Смаджа не изменился.

Важно: всегда стоит создавать геттеры и сеттеры.
Даже если на первый взгляд нет никаких ограничений на значения полей, такие вспомогательные методы точно не повредят.

Представьте ситуацию: вы и ваши коллеги вместе пишете программу. Вы создаете класс Cat с открытыми полями. Все радостно используют их напрямую.
А потом в какой-то момент вы понимаете: “Черт! Кто-то может случайно поставить отрицательный вес! Нужно срочно закрыть поля и добавить сеттеры!”

Вы делаете это… и в результате ломается весь код ваших коллег.

Ведь они уже написали кучу строк вроде:

cat.name = "Behemoth";

А теперь поля стали приватными, и компилятор выдает кучу ошибок!

cat.name = "Behemoth";// ошибка! Поле name класса Cat теперь приватное!

Чтобы избежать таких проблем, лучше сразу скрывать поля и использовать геттеры и сеттеры. Тогда все бы с самого начала обращались к полям только через методы. И если вдруг позже понадобилась бы какая-то проверка, вы бы просто добавили ее внутрь сеттера – и никто бы не пострадал.

Кстати: если вы хотите, чтобы поле было только доступным для чтения, можно просто создать только геттер без сеттера.

И последнее: ваши данные должны быть скрыты. Открыты должны быть только методы.

Можно представить это так:
Вы покупаете мобильный телефон.
И производитель не дает вам доступ к микросхемам и проводам внутри корпуса.
Вместо этого он дает вам простой понятный интерфейс: набрал номер, нажал зеленую кнопку – и звонок пошел.
Вам не нужно знать, что происходит внутри телефона. Главное – все работает, и вы ничего не сломаете.

То же самое и с классами: данные скрыты, наружу выставлены только методы.

Чтобы закрепить полученные знания, мы предлагаем вам посмотреть видеоурок:

Перевод статьи «Getters and setters».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх