🔥 🚀 Важно для всех, кто работает с 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».