Привет! Разберемся, что такое геттеры и сеттеры в Java и зачем они нужны.
Коротко: геттер это public-метод, который возвращает значение приватного поля, а сеттер это public-метод, который задает полю значение и может проверить его на корректность. Поля класса закрывают модификатором
private, а доступ к ним дают только через эти методы. Так работает инкапсуляция: данные защищены, а снаружи доступен только безопасный интерфейс.
В прошлых лекциях ты уже научился создавать собственные полноценные классы, с полями и методами. Это серьезный прогресс, молодец! Но есть нюанс: до сих пор мы создавали классы не совсем правильно. Сейчас разберемся почему.
Кратко
- Публичные поля (
public) можно менять из любого места программы, в том числе задать им некорректные значения.
- Решение: поля делаем
private (закрываем), а доступ даем через public-методы.
- Геттер (get) возвращает значение поля.
- Сеттер (set) задает значение поля и может проверить его на корректность.
- В сеттер можно встроить проверку (например, запретить отрицательный возраст).
- Геттеры и сеттеры создают сразу, даже без проверок, чтобы потом не ломать чужой код.
В чем проблема с публичными полями?
Никаких ошибок, на первый взгляд, в этом классе нет:
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("Мяу!");
}
}
На самом деле есть. Представь себе, что сидя на работе ты написал вот такой класс
Cat, обозначающий кошек. И ушел домой.
Пока тебя не было, другой программист пришел на работу, создал свой класс
Main, где начал использовать написанный тобой класс
Cat.
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Неважно, зачем он это сделал и как так получилось: может, устал человек или не выспался.
Важно другое: наш текущий класс
Cat позволяет присваивать полям безумные значения. В результате в программе находятся объекты с некорректным состоянием, типа вот этой кошки с возрастом -1000 лет.
Какую же ошибку мы в итоге допустили?
При создании класса мы открыли его данные.
Поля
name,
age и
weight находятся в публичном доступе. К ним можно обратиться в любом месте программы: достаточно просто создать объект
Cat, и все, у любого программиста есть доступ к его данным напрямую через оператор "
."
Cat cat = new Cat();
cat.name = "";
Здесь мы напрямую получаем доступ к полю
name и устанавливаем для него значение.
Нам нужно как-то защитить наши данные от некорректного вмешательства извне.
Что же для этого нужно?
Закрываем данные через private
Во-первых, все переменные экземпляра (поля) необходимо помечать модификатором
private. 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("Мяу!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";//ошибка! Поле name в классе Cat имеет private-доступ!
}
}
Компилятор это видит и сразу выдает ошибку.
Теперь поля вроде как защищены. Но получается, что доступ к ним закрыт "намертво": в программе нельзя даже получить вес существующей кошки, если это понадобится. Это тоже не вариант: в таком виде наш класс практически нельзя использовать.
В идеале нам нужно позволить какой-то ограниченный доступ к данным:
- Другие программисты должны иметь возможность создавать объекты
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("Мяу!");
}
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;
}
}
Как видишь, все довольно просто :) Их названия чаще всего состоят из слова
get/set плюс названия поля, за которое они отвечают.
Разницу между геттером и сеттером удобно свести в таблицу:
| Признак | Геттер (get) | Сеттер (set) |
| Назначение | вернуть значение поля | задать значение поля |
| Возвращает | тип поля | ничего (void) |
| Принимает | нет аргументов | новое значение (аргумент) |
| Проверка данных | обычно нет | можно встроить (например, запрет отрицательного возраста) |
| Пример имени | getName() | setName(String name) |
Как работают геттеры
Например, метод
getWeight() возвращает значение поля
weight у того объекта, для которого он был вызван.
Вот как это выглядит в программе:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Барсик", 5, 4);
String barsikName = barsik.getName();
int barsikAge = barsik.getAge();
int barsikWeight = barsik.getWeight();
System.out.println("Имя кота: " + barsikName);
System.out.println("Возраст кота: " + barsikAge);
System.out.println("Вес кота: " + barsikWeight);
}
}
Вывод в консоль:
Имя кота: Барсик
Возраст кота: 5
Вес кота: 4
Теперь из другого класса (
Main) есть доступ к полям
Cat, но
только через геттеры. Обрати внимание: у геттеров стоит модификатор доступа
public, то есть они доступны из любой точки программы.
Как работают сеттеры
А как обстоят дела с присваиванием значений? За это отвечают методы-
сеттеры
public void setName(String name) {
this.name = name;
}
Их работа, как видишь, тоже проста. Мы вызываем метод
setName() у объекта
Cat, передаем ему в качестве аргумента строку, и эта строка присваивается в поле
name нашего объекта.
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Барсик", 5, 4);
System.out.println("Изначальное имя кота: " + barsik.getName());
barsik.setName("Василий");
System.out.println("Новое имя кота: " + barsik.getName());
}
}
Здесь мы использовали и геттеры, и сеттеры. Вначале с помощью геттера мы получили и вывели в консоль первоначальное имя кота. Потом с помощью сеттера назначали его полю
name новое значение, "Василий". И потом с помощью геттера получили имя снова, чтобы проверить, действительно ли оно изменилось.
Вывод в консоль:
Изначальное имя кота: Барсик
Новое имя кота: Василий
Казалось бы, в чем разница? Мы также можем присваивать полям объекта некорректные значения, даже если у нас есть сеттеры:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Барсик", 5, 4);
barsik.setAge(-1000);
System.out.println("Возраст Барсика: " + barsik.getAge() + " лет");
}
}
Вывод в консоль:
Возраст Барсика: -1000 лет
Разница в том, что
сеттер это полноценный метод. А в метод, в отличие от поля, ты можешь заложить необходимую тебе логику проверки, чтобы не допустить неприемлемых значений. Например, можно легко не позволить назначение отрицательного числа в качестве возраста:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Ошибка! Возраст не может быть отрицательным числом!");
}
}
И теперь наш код работает корректно!
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Барсик", 5, 4);
barsik.setAge(-1000);
System.out.println("Возраст Барсика: " + barsik.getAge() + " лет");
}
}
Вывод в консоль:
Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика: 5 лет
Внутри сеттера есть ограничение, и оно защищает от попытки установить некорректные данные. Возраст Барсика остался без изменений.
Почему геттеры и сеттеры нужно создавать сразу
Геттеры и сеттеры нужно создавать всегда. Даже если в твоих полях нет ограничений на возможные значения, вреда от них не будет.
Представь себе ситуацию: ты и твои коллеги пишете программу вместе. Ты создал класс
Cat с публичными полями, и все программисты пользуются ими, как хотят. И тут в один прекрасный день до тебя доходит: "Блин, а ведь рано или поздно кто-то может нечаянно присвоить отрицательное число в переменную
weight! Надо создать сеттеры и сделать все поля приватными!"
Ты их создаешь, и весь написанный твоими коллегами код мгновенно ломается. Ведь они уже написали кучу кода, где обращались к полям
Cat напрямую.
cat.name = "Бегемот";
А теперь поля стали приватными и компилятор выдает кучу ошибок!
cat.name = "Бегемот";//ошибка! Поле name класса Cat имеет private-доступ!
В такой ситуации лучше было бы скрыть поля и создать геттеры-сеттеры
с самого начала. Все твои коллеги пользовались бы ими, и если бы тебя поздно "осенило", что надо ограничить значения полей, ты бы просто дописал проверку внутри сеттера. И ни у кого не сломался бы уже написанный код.
Конечно, если ты хочешь, чтобы доступ к какому-то полю был только "на чтение", можно создать для него один геттер.
"Снаружи", то есть за пределами твоего класса, должны быть доступны только методы. Данные должны быть скрыты.
Можно привести аналогию с мобильным телефоном. Представь, что вместо обычного включенного мобильника тебе дали телефон со вскрытым корпусом, где все провода, схемы и т.д. торчат наружу. Телефон при этом работает: если сильно постараться и потыкаться в схемы, может даже получится совершить звонок. Но скорее ты его просто сломаешь.
Вместо этого компания-производитель дает тебе интерфейс: клиент просто набирает нужные цифры, нажимает зеленую кнопку с трубкой, и звонок начинается. А уж что там происходит внутри со схемами и проводами и как они выполняют свою задачу, ему без разницы.
В этом примере компания ограничила доступ к "внутренностям" (данным) телефона и оставила снаружи только интерфейс (методы). В итоге клиент получит то, что хотел (совершить звонок) и точно ничего не сломает внутри.
Это и есть
инкапсуляция, один из ключевых принципов ООП. Подробнее о модификаторах доступа в Java рассказано в
документации Oracle (Java Tutorials).
Вопросы и ответы
Что такое геттеры и сеттеры в Java?
Геттер это public-метод, который возвращает значение приватного поля, а сеттер это public-метод, который задает полю значение. Через них дают контролируемый доступ к закрытым (private) полям объекта.
Зачем нужны геттеры и сеттеры, если есть публичные поля?
Публичное поле можно изменить откуда угодно и присвоить ему некорректное значение. Геттеры и сеттеры позволяют закрыть поля через private и контролировать доступ, а в сеттер встроить проверку значений (например, запрет отрицательного возраста).
Что такое инкапсуляция?
Это принцип ООП: скрыть внутренние данные объекта (поля) и оставить снаружи только методы. Данные защищены, а работать с объектом можно через безопасный интерфейс.
Как сделать поле доступным только для чтения?
Создать для него только геттер и не создавать сеттер. Тогда значение поля можно прочитать снаружи, но нельзя изменить.
Как называть геттеры и сеттеры?
Обычно это слово get или set плюс имя поля с большой буквы: для поля name это getName() и setName(). Геттер возвращает тип поля, а сеттер принимает значение как аргумент.
Почему геттеры и сеттеры советуют создавать сразу?
Если сначала сделать поля публичными, а потом закрыть их и добавить методы, весь код, который обращался к полям напрямую, сломается. Если создать методы сразу, проверки можно будет добавить позже, не ломая чужой код.
Читайте также
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ