Эта статья написанаyanglbme Оригинал, впервые опубликованный в публичном аккаунте"Сообщество открытого исходного кода Doocs", добро пожаловать на перепечатку.
Геттеры/сеттеры широко используются в Java. Казалось бы, просто, но не каждый Java-разработчик понимает и хорошо реализует методы Getter/Setter. Итак, в этой статье я хочу подробно обсудить методы получения и установки в Java, пожалуйста, следуйте за мной.
простой пример
В следующем коде показано основное использование методов Getter/Setter.
public class GetterAndSetterExample {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Как видите, мы в классеGetterAndSetterExample
Имя частной переменной объявлено в . Поскольку name является закрытым, мы не можем напрямую обращаться к переменной вне класса. Следующий код не будет компилироваться:
GetterAndSetterExample object = new GetterAndSetterExample();
object.name = "yanglbme"; // 编译出错
String name = object.name; // 编译出错
Правильная "поза" - вызвать геттераgetName()
и сеттерыsetName()
чтобы прочитать или обновить переменную:
GetterAndSetterExample object = new GetterAndSetterExample();
object.setName("yanglbme");
String name = object.getName();
Зачем нужны геттеры/сеттеры?
Используя методы Getter/Setter, доступ к переменным (get) и обновление (set) станут управляемыми. Рассмотрим код для следующего метода Setter:
public void setName(String name) {
if (name == null || "".equals(name)) {
throw new IllegalArgumentException();
}
this.name = name;
}
Это гарантирует, что значение, установленное по имени, никогда не будет пустым. Если вы можете напрямую пройти.
Оператор устанавливает значение имени, затем вызывающая сторона может установить любое значение имени по своему желанию, что нарушает ненулевое ограничение переменной имени.
То есть методы Getter/Setter гарантируют, что значение переменной защищено от случайных изменений из внешнего мира (код вызывающего объекта). когда переменнаяprivate
Модификатор "обернут", когда он скрыт и доступен только через геттеры и сеттеры. Инкапсуляция — одна из фундаментальных особенностей объектно-ориентированного программирования (ООП), а реализация геттеров/сеттеров — один из способов обеспечить инкапсуляцию в программном коде.
Именование ограничений для методов получения/установки
Именование сеттеров и геттеров должно следовать соглашениям об именах Java-бинов, таким какsetXxx()
иgetXxx()
, где Xxx — имя переменной:
public void setName(String name) { }
public String getName() { } // getter
И если переменная имеет тип boolean, метод получения может называться isXxx() или getXxx(), но первое предпочтительнее:
private boolean single;
public boolean isSingle() { } // getter
Часто неправильные реализации геттеров/сеттеров
Ошибка 1: Реализация методов Getter/Setter без строгих ограничений области видимости переменных
Как показано в следующем фрагменте кода:
public String name; // 使用public修饰
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
Имя переменной объявлено общедоступным, поэтому мы можем использовать точку непосредственно вне класса..
оператор для доступа к нему, аннулируя сеттеры и геттеры. в таком случаеРешениеПросто используйте более «строгие» модификаторы доступа, такие как protected и private.
Ошибка 2: Назначение ссылки на объект непосредственно в Setter
Рассмотрим следующий метод Setter:
public class Student {
private int[] scores;
// setter
public void setScores(int[] scores) {
this.scores = scores;
}
public void showScores() {
for (int score : scores) {
System.out.print(score + " ");
}
System.out.println();
}
}
Чувствуете себя хорошо? Давайте еще раз посмотрим на следующий код:
int[] myScores = {100, 97, 99, 88, 69};
Student yang = new Student();
yang.setScores(myScores);
yang.showScores();
Как видите, массив целых чисел myScores инициализируется первым и передается вsetScores()
метод, за которым следует простая печать scores , что дает следующий результат:
100 97 99 88 69
Теперь мы изменяем значение 2-го элемента в массиве myScores и снова печатаем баллы:
myScores[1] = 101;
yang.showScores();
Программа выведет следующее:
100 101 99 88 69
А это означает, что мы можем изменять данные вне метода Setter, что явно противоречит цели инкапсуляции Setter. Почему это так? давайте посмотрим еще разsetScores()
метод:
public void setScores(int[] scores) {
this.scores = scores;
}
Оценка переменной-члена напрямую ссылается на переменную параметраscores
, что означает, что обе переменные указывают на один и тот же объект в памяти, т.е.myScores
объект массива. Таким образом, изменения, внесенные в переменную myScores, вызовут синхронное изменение оценок переменных-членов. В этой ситуации,РешениеДа: скопируйте копию оценок параметра метода и назначьте ее переменной-члену scores:
public void setScores(int[] scores) {
this.scores = new int[scores.length];
System.arraycopy(scores, 0, this.scores, 0, scores.length);
}
Сводка опыта: если мы передаем объект в качестве параметра методу установки, не используйте метод простого присваивания ссылки напрямую. Вместо этого мы должны найти способ присвоить значение объекта внутренней переменной-члену, например, используя
System.arraycopy()
Метод копирует элементы из одного массива в другой.
Ошибка 3: Прямой возврат ссылки на объект
Рассмотрим реализацию следующего метода Getter:
private int[] scores;
public int[] getScores() {
return scores;
}
В программе мы называем метод GetScores () и изменим значение одного из элементов:
int[] myScores = {100, 97, 99, 88, 69};
Student yang = new Student();
yang.setScores(myScores);
yang.showScores();
int[] copyScores = yang.getScores();
copyScores[3] = 520;
yang.showScores();
выдаст следующий вывод:
100 97 99 88 69
100 97 99 520 69
Как видите, 4-й элемент массива был изменен на 520. Это связано с тем, что метод Getter напрямую возвращает ссылку на оценки внутренних переменных-членов, поэтому внешний код может получить ссылку и изменить элемент.
в таком случаеРешениеДа: должна быть возвращена копия объекта, а не ссылка напрямую:
public int[] getScores() {
int[] copy = new int[this.scores.length];
System.arraycopy(this.scores, 0, copy, 0, copy.length);
return copy; // 返回副本
}
Сводка опыта: не возвращайте ссылку на исходный объект в методе Getter. Вместо этого он должен возвращать копию исходного объекта.
Реализовать методы Getter/Setter для примитивных типов.
В Java основными типами являются int, float, double, boolean, char..., вы можете напрямую устанавливать или возвращать значение свободно, потому что Java копирует значение базовой переменной в другую переменную, а не в объект. Ссылки, поэтому, ошибок два и три можно легко избежать.
private float amount;
public void setAmount(float amount) {
this.amount = amount;
}
public float getAmount() {
return amount;
}
То есть для примитивных типов данных нет особых ухищрений для корректной реализации геттеров/сеттеров.
Реализовать методы Getter/Setter для типов объектов
Методы получения/установки для объектов String
String — это тип объекта, но он неизменяем, а это означает, что после создания объекта String мы не можем изменить его содержимое. Другими словами, каждое изменение объекта String приводит к созданию нового объекта String. Итак, подобно примитивным типам, мы можем безопасно реализовать геттеры/сеттеры для строковых переменных, например:
private String address;
public void setAddress(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
Методы Getter/Setter для объектов Date
java.util.Date
Класс реализует метод clone() из класса Object. Метод clone() возвращает копию объекта, поэтому мы можем использовать его для геттеров и сеттеров, как показано в следующем коде:
private Date birthDate;
public void setBirthDate(Date birthDate) {
this.birthDate = (Date) birthDate.clone();
}
public Date getBirthDate() {
return (Date) this.birthDate.clone();
}
clone()
Метод возвращает объект типа Object, поэтому мы должны привести его к типу Date.
Методы Getter/Setter объектов Collection
Для объектов Collection, как описано в ошибках 2 и 3 выше, мы не можем просто реализовать методы Getter/Setter.
private List<String> listTitles;
public void setListTitles(List<String> titles) {
this.listTitles = titles;
}
public List<String> getListTitles() {
return listTitles;
}
Для коллекций строкРешениезаключается в использовании конструктора, который получает другую коллекцию в качестве параметра. Например:
public void setListTitles(List<String> titles) {
// 将titles传递给ArrayList的构造函数
this.listTitles = new ArrayList<String>(titles);
}
public List<String> getListTitles() {
return new ArrayList<String>(this.listTitles);
}
Уведомление, вышеуказанный метод строительстваПрименяется только к коллекциям строкового типа. Но не для коллекций типа Object. Рассмотрим следующий пример, где мы определяем классCollectionGetterSetterObject
иPerson
:
import java.util.*;
public class CollectionGetterSetterObject {
// 元素类型是Person的List集合
private List<Person> listPeople;
public void setListPeople(List<Person> list) {
this.listPeople = new ArrayList<Person>(list);
}
public List<Person> getListPeople() {
return new ArrayList<Person>(this.listPeople);
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
Для String каждая копия объекта String создает для него новый объект, а другие объекты типа Object — нет, они просто копируют ссылку, поэтому две коллекции отличаются, но содержат один и тот же объект.
Глядя на Collection API, мы обнаруживаем, что ArrayList, HashMap, HashSet и т. д. реализуют собственный метод clone(). Эти методы возвращают поверхностные копии, которые не копируют элементы из исходной коллекции в целевую.
Например, Javadoc для метода clone() класса ArrayList описывает следующее:
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() { }
Поэтому мы не можем использовать метод clone() этих классов Collection.решениезаключается в реализации метода clone() для нашего собственного определенного объекта (класса Person в приведенном выше примере). Мы переопределяем метод clone() в классе Person следующим образом:
public Object clone() {
Person aClone = new Person(this.name);
return aClone;
}
Метод Setter для listPeople следует изменить следующим образом:
public void setListPeople(List<Person> list) {
for (Person aPerson : list) {
this.listPeople.add((Person) aPerson.clone());
}
}
И соответственно, метод Getter следует модифицировать следующим образом:
public List<Person> getListPeople() {
List<Person> listReturn = new ArrayList<Person>();
for (Person aPerson : this.listPeople) {
listReturn.add((Person) aPerson.clone());
}
return listReturn;
}
Таким образом, новыйCollectionGetterSetterObject
Код класса должен быть таким:
import java.util.*;
public class CollectionGetterSetterObject {
private List<Person> listPeople = new ArrayList<Person>();
public void setListPeople(List<Person> list) {
for (Person aPerson : list) {
this.listPeople.add((Person) aPerson.clone());
}
}
public List<Person> getListPeople() {
List<Person> listReturn = new ArrayList<Person>();
for (Person aPerson : this.listPeople) {
listReturn.add((Person) aPerson.clone());
}
return listReturn;
}
}
Подводя итог, ключевыми моментами для реализации Getter/Setter типа Collection являются:
- Для коллекций объектов String, поскольку объекты String являются неизменяемыми, специальной настройки не требуется.
- Для коллекций пользовательских типов объектов:
- Реализуйте метод clone() пользовательского типа.
- Для установщика добавьте клонированный элемент из исходной коллекции в целевую коллекцию.
- Для геттера создайте новую коллекцию и верните ее. Добавьте клонированные элементы из исходной коллекции в новую коллекцию.
Методы получения/установки для пользовательских объектов
Если вы определяете пользовательский тип объекта, вы должны реализовать метод clone() для вашего собственного типа.
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
// 自己实现clone方法
public Object clone() {
Person aClone = new Person(this.name);
return aClone;
}
}
Как мы видим, класс Person реализует свой метод clone() для возврата клонированной версии самого себя. Затем метод установки должен быть реализован следующим образом:
public void setFriend(Person person) {
this.friend = (Person) person.clone();
}
И для метода получения:
public Person getFriend() {
return (Person) this.friend.clone();
}
Подводя итог, правила реализации геттеров и сеттеров для пользовательских типов объектов таковы:
- Реализуйте метод clone() для пользовательских типов.
- Возвращает клонированный объект из геттера.
- Разместите клонированный объект в сеттере.
Суммировать
Геттеры/сеттеры Java выглядят простыми, но при неправильной реализации они могут быть опасными и даже стать источником проблем, которые заставят ваш код работать неправильно. Или, что еще хуже, кто-то может легко «опустошить» вашу программу, неявно манипулируя параметрами геттера или сеттера и получая от них объекты.
Пожалуйста, используйте его осторожно, чтобы не наступить на яму.
Добро пожаловать в мою общедоступную учетную запись WeChat «Сообщество открытого исходного кода Doocs». Оригинальные технические статьи будут опубликованы как можно скорее.