Jak tworzyć niezmienne obiekty w Javie?
Które obiekty należy nazwać niezmiennymi?
Jeśli mam klasę ze wszystkimi statycznymi elementami członkowskimi, czy jest ona niezmienna?
Jak tworzyć niezmienne obiekty w Javie?
Które obiekty należy nazwać niezmiennymi?
Jeśli mam klasę ze wszystkimi statycznymi elementami członkowskimi, czy jest ona niezmienna?
Odpowiedzi:
Poniżej znajdują się twarde wymagania dotyczące niezmiennego obiektu.
final
ale obiekt nadal może być zmienny, tj private final Date imStillMutable
.). Powinieneś zrobić defensive copies
w takich przypadkach.Powód stworzenia klasy final
jest bardzo subtelny i często pomijany. Jeśli nie jest to wersja ostateczna, ludzie mogą swobodnie rozszerzać twoją klasę, przesłonić public
lub protected
zachować, dodać zmienne właściwości, a następnie dostarczyć ich podklasę jako substytut. Deklarując klasę, final
możesz mieć pewność, że tak się nie stanie.
Aby zobaczyć problem w akcji, rozważ poniższy przykład:
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;
}
}
W praktyce powyższy problem jest bardzo często spotykany w środowiskach Dependency Injection. Nie tworzysz instancji w sposób jawny, a otrzymana referencja do superklasy może w rzeczywistości być podklasą.
Wniosek jest taki, że aby uzyskać twarde gwarancje niezmienności, musisz oznaczyć klasę jako final
. Jest to szczegółowo omówione w efektywnej Javie Joshua Blocha i wyraźnie przywołane w specyfikacji modelu pamięci Java .
Po prostu nie dodawaj publicznych metod mutatora (ustawiającego) do klasy.
private
lub klasa powinna być final
. Tylko po to, aby uniknąć dziedziczenia. Ponieważ dziedziczenie narusza hermetyzację.
Klasy nie są niezmienne, ale obiekty są.
Niezmienne środki: mój stan widoczny publicznie nie może się zmienić po inicjalizacji.
Pola nie muszą być uznawane za ostateczne, chociaż może to bardzo pomóc w zapewnieniu bezpieczeństwa wątków
Jeśli twoja klasa ma tylko statyczne składowe, to obiekty tej klasy są niezmienne, ponieważ nie możesz zmienić stanu tego obiektu (prawdopodobnie nie możesz też go utworzyć :))
Aby uczynić klasę niezmienną w języku Java, można zwrócić uwagę na następujące punkty:
1. Nie należy udostępniać metod ustawiających do modyfikowania wartości żadnej ze zmiennych instancji tej klasy.
2. Zadeklaruj klasę jako „ostateczną” . Zapobiegałoby to rozszerzaniu jej przez jakąkolwiek inną klasę, a tym samym zastępowaniu jej metody, która mogłaby modyfikować wartości zmiennych instancji.
3. Zadeklaruj zmienne instancji jako prywatne i ostateczne .
4. Można również zadeklarować konstruktor klasy jako prywatny i w razie potrzeby dodać metodę fabryczną w celu utworzenia instancji klasy.
Te punkty powinny pomóc !!
private
?
Z witryny Oracle , jak tworzyć niezmienne obiekty w Javie.
- Nie podawaj metod „ustawiających” - metod, które modyfikują pola lub obiekty, do których odwołują się pola.
- Spraw, aby wszystkie pola były ostateczne i prywatne.
- Nie zezwalaj podklasom na przesłanianie metod. Najprostszym sposobem na to jest zadeklarowanie klasy jako ostatecznej. Bardziej wyrafinowanym podejściem jest uczynienie konstruktora prywatnym i konstruowanie instancji w metodach fabrycznych.
- Jeśli pola instancji zawierają odniesienia do obiektów
modyfikowalnych , nie zezwalaj na ich zmianę: I. Nie udostępniaj metod modyfikujących obiekty podlegające zmianom .
II. Nie udostępniaj odniesień do zmiennych obiektów. Nigdy nie przechowuj odniesień do zewnętrznych, modyfikowalnych obiektów przekazanych do konstruktora; w razie potrzeby utwórz kopie i przechowuj odniesienia do kopii. Podobnie, w razie potrzeby utwórz kopie swoich wewnętrznych obiektów podlegających zmianom, aby uniknąć zwracania oryginałów w metodach.
Niezmienny obiekt to taki, który po utworzeniu nie zmieni swojego stanu wewnętrznego. Są bardzo przydatne w aplikacjach wielowątkowych, ponieważ można je udostępniać między wątkami bez synchronizacji.
1. Nie dodawaj żadnej metody ustawiającej
Jeśli budujesz niezmienny obiekt, jego stan wewnętrzny nigdy się nie zmieni. Zadaniem metody ustawiającej jest zmiana wartości wewnętrznej pola, więc nie można go dodać.
2. Zadeklaruj wszystkie pola jako ostateczne i prywatne
Pole prywatne nie jest widoczne spoza klasy, więc nie można do niego wprowadzić żadnych ręcznych zmian.
Zadeklarowanie ostatecznej wartości pola zagwarantuje, że jeśli odwołuje się do wartości pierwotnej, wartość nigdy się nie zmieni, jeśli odwołuje się do obiektu, którego odniesienia nie można zmienić. To nie wystarczy, aby zapewnić, że obiekt z tylko prywatnymi polami końcowymi nie będzie zmienny.
3. Jeśli pole jest zmiennym obiektem, utwórz jego kopie obronne dla metod pobierających
Widzieliśmy już wcześniej, że zdefiniowanie pola ostatecznego i prywatnego nie wystarczy, ponieważ można zmienić jego stan wewnętrzny. Aby rozwiązać ten problem, musimy utworzyć kopię obronną tego pola i zwracać to pole za każdym razem, gdy jest ono wymagane.
4. Jeśli zmienny obiekt przekazany do konstruktora musi być przypisany do pola, utwórz jego obronną kopię
Ten sam problem występuje, jeśli trzymasz referencję przekazaną do konstruktora, ponieważ można ją zmienić. Więc trzymanie odniesienia do obiektu przekazanego do konstruktora może tworzyć zmienne obiekty. Aby rozwiązać ten problem, konieczne jest utworzenie kopii obronnej parametru, jeśli są to obiekty zmienne.
Zauważ, że jeśli pole jest odwołaniem do niezmiennego obiektu, nie jest konieczne tworzenie jego kopii obronnych w konstruktorze i metodach pobierających, wystarczy zdefiniować pole jako końcowe i prywatne.
5. Nie zezwalaj podklasom na przesłanianie metod
Jeśli podklasa przesłania metodę, może zwrócić oryginalną wartość zmiennego pola zamiast jego obronnej kopii.
Jeśli zastosujesz się do tych prostych zasad, możesz swobodnie udostępniać swoje niezmienne obiekty między wątkami, ponieważ są one bezpieczne dla wątków!
Nie ma dobra ani zła, zależy tylko od tego, co wolisz. Zależy to tylko od twoich preferencji i od tego, co chcesz osiągnąć (a możliwość łatwego stosowania obu podejść bez zrażania zagorzałych fanów jednej lub drugiej strony jest świętym Graalem, którego szukają niektóre języki).
Przede wszystkim wiesz, dlaczego musisz stworzyć niezmienny obiekt i jakie są zalety niezmiennego obiektu.
Zalety niezmiennego obiektu
Współbieżność i wielowątkowość Automatycznie zabezpiecza wątki, więc problem z synchronizacją ... itd
Nie trzeba kopiować konstruktora Nie trzeba implementować clone. Nie można przesłonić klasy Uczyń pole jako prywatne i ostateczne elementy wywołujące wymuszanie konstruowania obiektu całkowicie w jednym kroku, zamiast używania konstruktora bez argumentów
Niezmienne obiekty to po prostu obiekty, których stan oznacza, że dane obiektu nie mogą ulec zmianie po skonstruowaniu niezmiennego obiektu.
proszę zobaczyć poniższy kod.
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();
}
}
Minimalizuj zmienność
Niezmienna klasa to po prostu klasa, której instancji nie można modyfikować. Wszystkie informacje zawarte w każdej instancji są dostarczane podczas jej tworzenia i są ustalane przez cały okres istnienia obiektu.
Niezmienne klasy JDK: ciąg, zapakowane klasy pierwotne (klasy opakowujące), BigInteger i BigDecimal itp.
Jak uczynić klasę niezmienną?
Rób kopie obronne. Zapewnij wyłączny dostęp do wszelkich zmiennych komponentów.
public List getList () {return Collections.unmodifiableList (list); <=== kopia obronna zmiennego pola przed zwróceniem go dzwoniącemu}
Jeśli Twoja klasa zawiera pola, które odwołują się do obiektów modyfikowalnych, upewnij się, że klienci klasy nie mogą uzyskać odwołań do tych obiektów. Nigdy nie inicjuj takiego pola w odniesieniu do obiektu dostarczonego przez klienta ani nie zwracaj odwołania do obiektu z metody dostępu.
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());
}
}
Aby uzyskać więcej informacji, zobacz mój blog:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
obiekt nazywany jest niezmiennym, jeśli jego stanu nie można zmienić po utworzeniu. Jednym z najprostszych sposobów tworzenia niezmiennej klasy w Javie jest ustawienie wszystkich jej pól jako ostatecznych. Jeśli potrzebujesz napisać niezmienną klasę, która zawiera zmienne klasy, takie jak "java.util.Date". W celu zachowania niezmienności w takich przypadkach zaleca się zwrot kopii oryginalnego przedmiotu,
Obiekty niezmienne to te obiekty, których stanu nie można zmienić po utworzeniu, na przykład klasa String jest niezmienną klasą. Niezmienne obiekty nie mogą być modyfikowane, więc są również bezpieczne wątkowo podczas wykonywania współbieżnego.
Cechy niezmiennych klas:
Klucze do napisania niezmiennej klasy:
Jeśli chcesz, aby jakakolwiek klasa była niezmienną klasą, należy wziąć pod uwagę kilka poniższych kroków.
Rzućmy okiem na to, co napisaliśmy powyżej:
//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());
}
}
Oprócz odpowiedzi udzielonej przez @ nsfyn55, należy również wziąć pod uwagę następujące aspekty niezmienności obiektu, które mają pierwszorzędne znaczenie
Rozważ następujące klasy:
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());
}
}
Poniżej będzie wynik z MutabilityCheck:
Foo
Bar
Ważne jest, aby to zauważyć,
Konstruowanie zmiennych obiektów na niezmiennym obiekcie (za pomocą konstruktora) przez „kopiowanie” lub „cloing” do zmiennych instancji elementu niezmiennego opisanego następującymi zmianami:
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;
}
}
nadal nie zapewnia całkowitej niezmienności, ponieważ z klasy MutabilityCheck nadal obowiązuje:
iMC.getMutClass().setName("Blaa");
Jednak uruchomienie MutabilityCheck ze zmianami wprowadzonymi w 1. spowoduje, że wynik będzie:
Foo
Foo
Aby osiągnąć całkowitą niezmienność obiektu, wszystkie jego obiekty zależne również muszą być niezmienne
Z JDK 14+, który ma JEP 359 , możemy użyć " records
". Jest to najprostszy i bezproblemowy sposób tworzenia klasy Immutable.
Klasa rekordu to płytko niezmienny , przezroczysty nośnik dla ustalonego zestawu pól, zwanego rekordem, components
który zawiera state
opis rekordu. Każda z nich component
daje początek final
polu, które przechowuje podaną wartość i accessor
metodę pobierania wartości. Nazwa pola i nazwa akcesora są zgodne z nazwą komponentu.
Rozważmy przykład tworzenia niezmiennego prostokąta
record Rectangle(double length, double width) {}
Nie ma potrzeby deklarowania żadnego konstruktora, nie ma potrzeby wdrażania equals
& hashCode
metod. Dowolne rekordy wymagają nazwy i opisu stanu.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Jeśli chcesz sprawdzić poprawność wartości podczas tworzenia obiektu, musimy jawnie zadeklarować konstruktora.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
Treść rekordu może deklarować metody statyczne, pola statyczne, inicjatory statyczne, konstruktory, metody wystąpień i typy zagnieżdżone.
Metody instancji
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
pola statyczne, metody
Ponieważ stan powinien być częścią komponentów, nie możemy dodawać pól instancji do rekordów. Ale możemy dodać statyczne pola i metody:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}