Як створити незмінні об'єкти в Java?
Які об’єкти слід назвати незмінними?
Якщо у мене є клас із усіма статичними членами, він незмінний?
Як створити незмінні об'єкти в Java?
Які об’єкти слід назвати незмінними?
Якщо у мене є клас із усіма статичними членами, він незмінний?
Відповіді:
Нижче наведені жорсткі вимоги незмінного об'єкта.
final
але об’єкт все одно може бути змінним, тобто private final Date imStillMutable
). Ви повинні зробити defensive copies
в цих випадках.Міркування, пов’язані із створенням класу final
, дуже тонкі і часто нехтуються. Якщо його не остаточні люди можуть вільно розширити ваш клас, замінити public
або protected
поведінку, додати змінні властивості, а потім надати свій підклас як заміну. Оголосивши клас, final
ви можете переконатися, що цього не станеться.
Щоб побачити проблему в дії, розгляньте приклад нижче:
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
На практиці дуже часто зустрічається з вищезазначеною проблемою в середовищах ін’єкції залежності. Ви не явно створюєте інстанції для речей, і посилання на супер клас, яке ви отримали, насправді може бути підкласом.
Заберіть що робити нелегкі гарантії про незмінність ви повинні помітити клас як final
. Це детально висвітлено в Ефективній Java Джошуа Блоха та чітко на нього посилається в специфікації для моделі пам'яті Java .
Тільки не додайте до класу загальнодоступні методи мутатора (сеттера).
private
або клас повинен бути final
. Тільки щоб уникнути спадкування. Оскільки успадкування порушує інкапсуляцію.
Класи не є незмінними, предмети є.
Незмінні засоби: мій загальнодоступний видимий стан не може змінитися після ініціалізації.
Поля не обов'язково оголошувати остаточними, хоча це може надзвичайно допомогти забезпечити безпеку потоків
Якщо у класі є лише статичні члени, то об'єкти цього класу незмінні, оскільки ви не можете змінити стан цього об'єкта (ви, мабуть, також не можете створити його :))
Щоб зробити клас незмінним у Java, ви можете
взяти до уваги наступні моменти: 1. Не надайте методів встановлення для модифікації значень будь-яких змінних екземпляра класу.
2. Оголосіть клас як „остаточний” . Це не дозволить будь-якому іншому класу розширити його, а отже, замінити будь-який метод із нього, який міг би змінити значення змінних екземпляра.
3. Оголосіть змінні екземпляра як приватні та остаточні .
4. Ви також можете оголосити конструктор класу приватним і додати фабричний метод для створення екземпляра класу, коли це потрібно.
Ці моменти повинні допомогти !!
private
?
З веб-сайту oracle , як створити незмінні об’єкти в Java.
- Не передбачайте методів "setter" - методів, які змінюють поля або об'єкти, на які посилаються поля.
- Зробіть усі поля остаточними та приватними.
- Не дозволяти підкласам замінювати методи. Найпростіший спосіб зробити це - оголосити клас остаточним. Більш складний підхід полягає в тому, щоб зробити конструктор приватним і побудувати екземпляри в заводських методах.
- Якщо поля екземпляра містять посилання на змінні об'єкти, не дозволяйте змінювати ці об'єкти:
I. Не надайте методів, що змінюють змінні об'єкти.
II. Не діліться посиланнями на змінні об'єкти. Ніколи не зберігайте посилання на зовнішні, змінні об'єкти, передані конструктору; при необхідності створюйте копії та зберігайте посилання на них. Подібним чином створюйте копії внутрішніх змінних об’єктів, коли це необхідно, щоб уникнути повернення оригіналів у ваших методах.
Незмінний об’єкт - це об’єкт, який не змінить свого внутрішнього стану після створення. Вони дуже корисні в багатопотокових програмах, оскільки їх можна спільно використовувати між потоками без синхронізації.
1. Не додайте жодного методу встановлення
Якщо ви будуєте незмінний об'єкт, його внутрішній стан ніколи не зміниться. Завдання методу сетера - змінити внутрішнє значення поля, тому ви не можете його додати.
2. Оголосіть усі поля остаточними та приватними
Приватне поле не видно поза класом, тому жодні ручні зміни до нього застосувати не можна.
Оголошення поля остаточним гарантуватиме, що якщо воно посилається на примітивне значення, значення ніколи не зміниться, якщо воно посилається на об'єкт, посилання не може бути змінено. Цього недостатньо для того, щоб об’єкт із лише приватними кінцевими полями не мінявся.
3. Якщо поле є змінним об'єктом, створіть його захисні копії для отримання методів
Ми вже бачили, що визначення поля остаточного та приватного недостатньо, оскільки можна змінити його внутрішній стан. Для вирішення цієї проблеми нам потрібно створити захисну копію цього поля і повертати це поле кожного разу, коли воно запитується.
4. Якщо мутабельний об'єкт, переданий конструктору, повинен бути призначений полю, створіть його захисну копію
Така сама проблема трапляється, якщо ви тримаєте посилання, передане конструктору, оскільки його можна змінити. Отже, утримання посилання на об’єкт, переданий конструктору, може створити змінні об’єкти. Для вирішення цієї проблеми необхідно створити захисну копію параметра, якщо це змінні об'єкти.
Зверніть увагу, що якщо поле є посиланням на незмінний об'єкт, для створення захисних копій його в конструкторі немає необхідності, а в методах getter досить визначити поле як остаточне та приватне.
5. Не дозволяйте підкласам замінювати методи
Якщо підклас перевизначає метод, він може повернути початкове значення змінного поля замість захисної його копії.
Якщо ви дотримуєтесь цих простих правил, ви можете вільно ділитися своїми незмінними об'єктами між потоками, оскільки вони безпечні для потоків!
Немає правильного чи неправильного, це просто залежить від того, що ви віддаєте перевагу. Це лише залежить від ваших уподобань і від того, чого ви хочете досягти (а можливість легко використовувати обидва підходи, не відчужуючи завзятих шанувальників тієї чи іншої сторони, - це святий Грааль, якого шукають деякі мови).
Перш за все, ви знаєте, навіщо потрібно створювати незмінний об'єкт, і які переваги незмінного об'єкта.
Переваги незмінного об'єкта
Паралельність і багатопотоковість Це автоматично безпечно для потоків, тому проблема синхронізації .... тощо
Не потрібно копіювати конструктор Не потрібно реалізовувати клон. Клас неможливо замінити. Зробити поле приватним і остаточним викликом, щоб повністю побудувати об’єкт за один крок, замість використання конструктора no-Argument
Незмінні об'єкти - це просто об'єкти, стан яких означає, що дані об'єкта не можуть змінюватися після побудови незмінного об'єкта.
будь ласка, дивіться наведений нижче код.
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
Мінімізуйте мінливість
Незмінний клас - це просто клас, екземпляри якого неможливо змінити. Вся інформація, що міститься в кожному екземплярі, надається під час її створення та фіксується протягом усього життя об’єкта.
Незмінні класи JDK: String, примітивні класи в упаковці (класи-обгортки), BigInteger та BigDecimal тощо.
Як зробити клас незмінним?
Зробіть захисні копії. Забезпечте ексклюзивний доступ до будь-яких змінних компонентів.
загальнодоступний список getList () {return Collections.unmodifiableList (list); <=== захисна копія поля, що змінюється, перед поверненням його абоненту}
Якщо ваш клас має будь-які поля, які посилаються на змінні об'єкти, переконайтеся, що клієнти класу не можуть отримати посилання на ці об'єкти. Ніколи не ініціалізуйте таке поле посиланням на об'єкт, надане клієнтом, або повертайте посилання на об'єкт від доступу.
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}
Для отримання додаткової інформації див. Мій блог:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
об'єкт називається незмінним, якщо його стан неможливо змінити після його створення. Одним з найпростіших способів створення незмінного класу в Java є встановлення всіх його полів остаточними. Якщо вам потрібно написати незмінний клас, який включає змінні класи, такі як "java.util.Date". Щоб зберегти незмінність у таких випадках, рекомендується повернути копію оригінального об'єкта,
Незмінні об'єкти - це ті об'єкти, стан яких неможливо змінити після їх створення, наприклад клас String є незмінним класом. Незмінні об'єкти не можна модифікувати, тому вони також безпечні для потоку при одночасному виконанні.
Особливості незмінних класів:
Ключі для запису незмінного класу:
Потрібно врахувати наступні кілька кроків, коли ви хочете, щоб будь-який клас був незмінним класом.
Давайте подивимось те, що ми ввели вище:
//ImmutableClass
package younus.attari;
public final class ImmutableExample {
private final String name;
private final String address;
public ImmutableExample(String name,String address){
this.name=name;
this.address=address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
//MainClass from where an ImmutableClass will be called
package younus.attari;
public class MainClass {
public static void main(String[] args) {
ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
System.out.println(example.getName());
}
}
Додавання до відповіді , наданої @ nsfyn55, такі аспекти також повинні бути розглянуті для об'єкта незмінності, які мають прайм значення
Розглянемо такі класи:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = mc;
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class MutabilityCheck {
public static void main(String[] args) {
MutableClass mc = new MutableClass();
mc.setName("Foo");
ImmutableClass iMC = new ImmutableClass(mc);
System.out.println(iMC.getMutClass().getName());
mc.setName("Bar");
System.out.println(iMC.getMutClass().getName());
}
}
Далі буде виведено результат з MutabilityCheck:
Foo
Bar
Важливо зазначити, що,
Побудова змінних об'єктів на незмінному об'єкті (за допомогою конструктора), або шляхом "копіювання", або "клонування" до змінних екземпляра незмінного, описаних наступними змінами:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = new MutableClass(mc);
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public MutableClass() {
}
//copy constructor
public MutableClass(MutableClass mc) {
this.name = mc.getName();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
все ще не забезпечує повну незмінність, оскільки з класу MutabilityCheck все ще діє:
iMC.getMutClass().setName("Blaa");
Однак запуск MutabilityCheck зі змінами, внесеними в 1., призведе до виводу:
Foo
Foo
Для досягнення повної незмінності об'єкта всі його залежні об'єкти також повинні бути незмінними
З JDK 14+, який має JEP 359 , ми можемо використовувати " records
". Це найпростіший і безшвидкий спосіб створення незмінного класу.
Клас запису - це дрібно незмінний , прозорий носій для фіксованого набору полів, відомий як запис, components
що забезпечує state
опис запису. Кожне component
з них створює final
поле, яке містить вказане значення, і accessor
спосіб отримання значення. Ім'я поля та ім'я доступу відповідають імені компонента.
Розглянемо приклад створення незмінного прямокутника
record Rectangle(double length, double width) {}
Не потрібно оголошувати будь-який конструктор, не потрібно реалізовувати equals
& hashCode
методи. Будь-які записи потребують імені та опису стану.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Якщо ви хочете перевірити значення під час створення об'єкта, ми повинні чітко оголосити конструктор.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
Тіло запису може оголошувати статичні методи, статичні поля, статичні ініціалізатори, конструктори, методи екземпляра та вкладені типи.
Методи інстанції
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
статичні поля, методи
Оскільки стан повинен бути частиною компонентів, ми не можемо додавати поля екземплярів до записів. Але ми можемо додати статичні поля та методи:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}