Java Language
Неизменяемый класс
Поиск…
Вступление
Неизменяемыми объектами являются экземпляры, состояние которых не изменяется после его инициализации. Например, String является неизменяемым классом, и после его создания значение никогда не изменяется.
замечания
Некоторые непреложные классы в Java:
- java.lang.String
- Классы-оболочки для примитивных типов: java.lang.Integer, java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Boolean, java.lang.Long, java.lang.Double, java.lang.Float
- Большинство классов перечисления неизменяемы, но это фактически зависит от конкретного случая.
- java.math.BigInteger и java.math.BigDecimal (по крайней мере, объекты самих этих классов)
- java.io.File. Обратите внимание, что это представляет объект, внешний по отношению к виртуальной машине (файл в локальной системе), который может или не может существовать, и имеет некоторые методы, изменяющие и запрашивающие состояние этого внешнего объекта. Но сам объект File остается неизменным.
Правила определения неизменяемых классов
Следующие правила определяют простую стратегию создания неизменяемых объектов.
- Не предоставляйте методы «setter» - методы, которые изменяют поля или объекты, на которые ссылаются поля.
- Сделайте все поля окончательными и частными.
- Не допускайте переопределение подклассов. Самый простой способ сделать это - объявить класс окончательным. Более сложный подход заключается в том, чтобы сделать конструктор частным и построить экземпляры в заводских методах.
- Если поля экземпляра включают ссылки на изменяемые объекты, не разрешайте изменять эти объекты:
- Не предоставляйте методы, изменяющие изменяемые объекты.
- Не используйте ссылки на изменяемые объекты. Никогда не храните ссылки на внешние, изменяемые объекты, переданные конструктору; при необходимости, создавать копии и хранить ссылки на копии. Аналогичным образом создайте копии своих внутренних изменяемых объектов, когда это необходимо, чтобы избежать возврата оригиналов в ваши методы.
Пример без изменяемых ссылок
public final class Color {
final private int red;
final private int green;
final private int blue;
private void check(int red, int green, int blue) {
if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public Color(int red, int green, int blue) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
}
public Color invert() {
return new Color(255 - red, 255 - green, 255 - blue);
}
}
Пример с mutable refs
В этом случае класс Point изменен, и некоторый пользователь может изменить состояние объекта этого класса.
class Point {
private int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
//...
public final class ImmutableCircle {
private final Point center;
private final double radius;
public ImmutableCircle(Point center, double radius) {
// we create new object here because it shouldn't be changed
this.center = new Point(center.getX(), center.getY());
this.radius = radius;
}
В чем преимущество неизменности?
Преимущество непреложности заключается в параллелизме. Трудно поддерживать правильность в изменяемых объектах, поскольку несколько потоков могут пытаться изменить состояние одного и того же объекта, приводя к тому, что некоторые потоки видят другое состояние одного и того же объекта, в зависимости от времени чтения и записи в упомянутый объект.
Имея неизменный объект, можно гарантировать, что все потоки, которые смотрят на объект, будут видеть одно и то же состояние, так как состояние неизменяемого объекта не изменится.