Onveranderlijke klasse?

Hoe kan men een Java-klasse onveranderlijk maken, wat is de noodzaak van onveranderlijkheid en heeft het enig voordeel om dit te gebruiken?


Antwoord 1, autoriteit 100%

Wat is een onveranderlijk object?

Een onveranderlijk object is een object dat niet van status verandert nadat het is geïnstantieerd.

Hoe maak je een object onveranderlijk?

Over het algemeen kan een onveranderlijk object worden gemaakt door een klasse te definiëren waarvan geen van zijn leden zichtbaar is en die geen setters heeft.

De volgende klasse maakt een onveranderlijk object:

class ImmutableInt {
  private final int value;
  public ImmutableInt(int i) {
    value = i;
  }
  public int getValue() {
    return value;
  }
}

Zoals te zien is in het bovenstaande voorbeeld, kan de waarde van de ImmutableIntalleen worden ingesteld wanneer het object is geïnstantieerd en door alleen een getter te hebben (getValue) de status van het object kan niet worden gewijzigd na het maken van een instantie.

Er moet echter voor worden gezorgd dat alle objecten waarnaar door het object wordt verwezen ook onveranderlijk moeten zijn, anders zou het mogelijk kunnen zijn om de status van het object te wijzigen.

Als u bijvoorbeeld toestaat dat een verwijzing naar een array of ArrayListwordt verkregen via een getter, kan de interne status veranderen door de array of verzameling te wijzigen:

class NotQuiteImmutableList<T> {
  private final List<T> list;
  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }
  public List<T> getList() {
    return list;
  }
}

Het probleem met de bovenstaande code is dat de ArrayListkan worden verkregen via getListen kan worden gemanipuleerd, wat ertoe leidt dat de status van het object zelf wordt gewijzigd. , niet onveranderlijk.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));
// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

Een manier om dit probleem te omzeilen is door een kopie van een array of verzameling te retourneren wanneer deze wordt aangeroepen door een getter:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

Wat is het voordeel van onveranderlijkheid?

Het voordeel van onveranderlijkheid komt met gelijktijdigheid. Het is moeilijk om de correctheid van veranderlijke objecten te behouden, omdat meerdere threads kunnen proberen de status van hetzelfde object te veranderen, wat ertoe kan leiden dat sommige threads een andere status van hetzelfde object zien, afhankelijk van de timing van het lezen en schrijven naar het genoemde object. voorwerp.

Door een onveranderlijk object te hebben, kan men ervoor zorgen dat alle threads die naar het object kijken dezelfde status zullen zien, aangezien de status van een onveranderlijk object niet zal veranderen.


Antwoord 2, autoriteit 14%

In aanvulling op de antwoorden die al zijn gegeven, raad ik aan om te lezen over onveranderlijkheid in Effective Java, 2nd Ed., aangezien er enkele details zijn die gemakkelijk over het hoofd worden gezien (bijv. defensieve kopieën). Plus, effectieve Java 2e editie. is een must-read voor elke Java-ontwikkelaar.


Antwoord 3, autoriteit 5%

Je maakt een klasse als volgt onveranderlijk:

public final class Immutable
{
    private final String name;
    public Immutable(String name) 
    {
        this.name = name;
    }
    public String getName() { return this.name; } 
    // No setter;
}

Hier volgen de vereisten om een ​​Java-klasse onveranderlijk te maken:

  • Klassemoet worden gedeclareerd als final(zodat er geen onderliggende klassen kunnen worden gemaakt)
  • Ledenin de klasse moeten worden gedeclareerd als final(zodat we de waarde ervan niet kunnen wijzigen na het maken van het object)
  • Schrijf Getter-methodenvoor alle variabelen erin om Leden-waarden
  • te krijgen

  • Geen Setters-methoden

Onveranderlijke klassen zijn handig omdat
– Ze zijn draadveilig.
– Ze drukken ook iets dieps uit over je ontwerp: “Kan dit niet veranderen.”, Als het van toepassing is, is het precies wat je nodig hebt.


Antwoord 4, autoriteit 4%

Onveranderlijkheid kan hoofdzakelijk op twee manieren worden bereikt:

  • het gebruik van finalinstantiekenmerken om hertoewijzing te voorkomen
  • een klasseninterface gebruiken die eenvoudigweg geen enkele bewerking toestaat die in staat is om te wijzigen wat zich in je klas bevindt (alleen gettersen geen setters

Voordelen van onveranderlijkheid zijn de veronderstellingen die je kunt maken over dit object:

  • je krijgt de regel zonder neveneffecten (die erg populair is in functionele programmeertalen) en stelt je in staat objecten gemakkelijker in een gelijktijdige omgeving te gebruiken, omdat je weet dat ze niet op een atomaire of niet-atomaire manier kunnen worden gewijzigd wanneer ze door veel threads worden gebruikt
  • implementaties van talen kunnen deze objecten op een andere manier behandelen, ze in geheugenzones plaatsen die worden gebruikt voor statische gegevens, waardoor deze objecten sneller en veiliger kunnen worden gebruikt (dit gebeurt binnen de JVM voor strings)

Antwoord 5, autoriteit 2%

Onveranderlijke klassen kunnen geen waarden opnieuw toewijzen nadat deze zijn geïnstantieerd. De constructor wijst waarden toe aan zijn privévariabelen. Totdat het object nul wordt, kunnen waarden niet worden gewijzigd vanwege het niet beschikbaar zijn van setter-methoden.

om onveranderlijk te zijn moet voldoen aan het volgende,

  • Alle variabelen moeten privatezijn.
  • Er zijn geen mutatormethoden(setters) voorzien.
  • Vermijd het overschrijven van de methode door de klasse definitief te maken (sterke onveranderlijkheid)
    of methoden definitief (week onveranderlijkheid).
  • Kloon diep als het niet-primitieve of veranderlijke klassen bevat.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {
// make the variables private
private String Name;
//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}
//provide getters to access values
public String getName() {
return this.Name;
}
}

Voordelen:
Onveranderlijke objecten bevatten zijn geïnitialiseerde waarden totdat het sterft.

java-immutable-classes-short-note


Antwoord 6, autoriteit 2%

Hoe kan men een Java-klasse onveranderlijk maken?

Vanaf JDK 14+ met JEP 359, kunnen we “records“. Het is de eenvoudigste en makkelijkste manier om een ​​onveranderlijke klas te creëren.

Een recordklasse is een oppervlakkige onveranderlijke, transparante drager voor een vaste set velden die bekend staat als de record componentsdie een state-beschrijving geeft Voor de goede orde. Elke componentgeeft aanleiding tot een finalveld dat de opgegeven waarde bevat en een accessormethode om de waarde op te halen. De veldnaam en de naam van de accessor komen overeen met de naam van het onderdeel.

Laten we eens kijken naar het voorbeeld van het maken van een onveranderlijke rechthoek

record Rectangle(double length, double width) {}

Het is niet nodig om een ​​constructor te declareren, het is niet nodig om equals & hashCode-methoden. Alle records hebben een naam en een staatsbeschrijving nodig.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Als u de waarde wilt valideren tijdens het maken van het object, moeten we de constructor expliciet declareren.

public Rectangle {
    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

De hoofdtekst van de record kan statische methoden, statische velden, statische initializers, constructors, instantiemethoden en geneste typen declareren.

Instance-methoden

record Rectangle(double length, double width) {
  public double area() {
    return this.length * this.width;
  }
}

statische velden, methoden

Omdat status onderdeel moet zijn van de componenten, kunnen we geen instantievelden aan records toevoegen. Maar we kunnen statische velden en methoden toevoegen:

record Rectangle(double length, double width) {
  static double aStaticField;
  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

wat is de noodzaak van onveranderlijkheid en is er enig voordeel om dit te gebruiken?

Eerder geposte antwoorden zijn goed genoeg om de noodzaak van onveranderlijkheid te rechtvaardigen en het heeft voordelen


Antwoord 7

Onveranderlijke klassezijn die waarvan de objecten niet kunnen worden gewijzigd na het maken.

Onveranderlijke klassen is handig voor

  • Caching doel
  • Gelijktijdige omgeving (ThreadSafe)
  • Moeilijk voor erfenis
  • Waarde kan in geen enkele omgeving worden gewijzigd

Voorbeeld

Stringklasse

Codevoorbeeld

public final class Student {
    private final String name;
    private final String rollNumber;
    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }
    public String getName() {
        return this.name;
    }
    public String getRollNumber() {
        return this.rollNumber;
    }
}

Antwoord 8

Een andere manier om een ​​onveranderlijk object te maken is met behulp van de Immutables.orgbibliotheek:

Ervan uitgaande dat de vereiste afhankelijkheden zijn toegevoegd, maakt u een samenvatting
class met abstracte accessor-methoden. U kunt hetzelfde doen door
annoteren met interfaces of zelfs annotaties (@interface):

package info.sample;
import java.util.List;
import java.util.Set;
import org.immutables.value.Value;
@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

Het is nu mogelijk om de gegenereerde onveranderlijke te genereren en vervolgens te gebruiken
implementatie:

package info.sample;
import java.util.List;
public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}
    int foo = value.foo(); // 2
    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}

Antwoord 9

Een onveranderlijke klasse is gewoon een klasse waarvan de instanties niet kunnen worden gewijzigd.

Alle informatie in elke instantie is vastgelegd voor de levensduur van het object, dus er kunnen nooit veranderingen worden waargenomen.

Onveranderlijke klassen zijn gemakkelijker te ontwerpen, implementeren en gebruiken dan veranderlijke klassen.

Volg deze vijf regels om een ​​klasse onveranderlijk te maken:

  1. Geef geen methoden die de status van het object wijzigen

  2. Zorg ervoor dat de les niet kan worden verlengd.

  3. Maak alle velden definitief.

  4. Maak alle velden privé.

  5. Zorg voor exclusieve toegang tot alle veranderlijke componenten.

Onveranderlijke objecten zijn inherent thread-safe; ze vereisen geen synchronisatie.

Onveranderlijke objecten kunnen vrij worden gedeeld.

Onveranderlijke objecten vormen geweldige bouwstenen voor andere objecten


Antwoord 10

@Jack, Het hebben van definitieve velden en setters in de klas maakt de klas niet onveranderlijk. final keyword zorg er alleen voor dat een variabele nooit opnieuw wordt toegewezen. U moet een diepe kopie van alle velden retourneren in getter-methoden. Dit zorgt ervoor dat, na het verkrijgen van een object van de getter-methode, de interne toestand van het object niet wordt verstoord.


Antwoord 11

Hier volgen de vereisten:

  • De klasse moet worden gedeclareerd als
    final(zodat er geen onderliggende klassen kunnen worden gemaakt)

  • Gegevensleden in de klas moeten worden gedeclareerd als final(zodat we
    verander de waarde ervan na het maken van het object)

  • Een geparametreerde constructormoet alle velden initialiseren
    een diepe kopie uitvoeren (zodat gegevensleden niet kunnen worden gewijzigd met
    objectreferentie)

  • Deep Copy van objecten moet worden uitgevoerd in de getter-methoden (To
    een kopie retourneren in plaats van de werkelijke objectreferentie terug te sturen)

  • Geen setters(Om niet de mogelijkheid te hebben om de waarde van de . te wijzigen)
    instantievariabele)

import java.util.HashMap;
import java.util.Map;
// An immutable class
public final class Student {
    private final String name;
    private final int regNo;
    private final Map<String, String> metadata;
    public Student(String name, int regNo,
                   Map<String, String> metadata)
    {
        this.name = name;
        this.regNo = regNo;
        Map<String, String> tempMap = new HashMap<>();
        for (Map.Entry<String, String> entry :
             metadata.entrySet()) {
            tempMap.put(entry.getKey(), entry.getValue());
        }
        this.metadata = tempMap;
    }
    public String getName() { return name; }
    public int getRegNo() { return regNo; }
    public Map<String, String> getMetadata()
    {
        Map<String, String> tempMap = new HashMap<>();
        for (Map.Entry<String, String> entry :
             this.metadata.entrySet()) {
            tempMap.put(entry.getKey(), entry.getValue());
        }
        return tempMap;
    }
}
// Driver class
class Test {
    public static void main(String[] args)
    {
        Map<String, String> map = new HashMap<>();
        map.put("1", "first");
        map.put("2", "second");
        Student s = new Student("ABC", 101, map);
        System.out.println(s.getName());
        System.out.println(s.getRegNo());
        System.out.println(s.getMetadata());
        // Uncommenting below line causes error
        // s.regNo = 102;
        map.put("3", "third");
        System.out.println(s.getMetadata()); // Remains unchanged due to deep copy in constructor
        s.getMetadata().put("4", "fourth");
        System.out.println(s.getMetadata()); // Remains unchanged due to deep copy in getter
    }
}

Antwoord 12

Als een niet-moedertaalspreker van het Engels, houd ik niet van de algemene interpretatie van “onveranderlijke klasse” als “geconstrueerde klasseobjecten zijn onveranderlijk”; ik neig er zelf toe om dat te interpreteren als “het klasseobject zelf is onveranderlijk”.

Dat gezegd hebbende, “onveranderlijke klasse” is een soort onveranderlijk object. Het verschil is bij het beantwoorden van wat het voordeel is. Voor zover ik weet/interpretatie verhindert de onveranderlijke klasse dat zijn objecten tijdens runtime-gedrag worden gewijzigd.


Antwoord 13

De meeste antwoorden hier zijn goed, en sommigen noemden de regels, maar ik vind het goed om in woorden uit te drukken waarom& wanneer we deze regels moeten volgen. Dus geef ik de onderstaande uitleg

  • Declareer de lidvariabelen als ‘definitief’ – Wanneer we ze als definitief declareren, dwingt de compiler ons om ze te initialiseren. We kunnen direct initialiseren, standaard constructor, door arg constructor. (zie voorbeeldcode hieronder)
    en na initialisatie kunnen we ze niet meer wijzigen omdat ze definitief zijn.
  • En natuurlijk, als we Setters proberen te gebruiken voor die laatste variabelen, geeft de compiler een fout.

    public class ImmutableClassExplored {
        public final int a; 
        public final int b;
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

Moeten we de les als ‘final’ declareren?

  • Zonder final keyword in class-declaratie kan class worden geërfd. Dus de subklasse kan gettermethoden overschrijven. Hier moeten we twee scenario’s overwegen:

1. Alleen primitieve leden hebben:we hebben geen probleem. Als een klas alleen primitieve leden heeft, hoeven we de klas niet als definitief te verklaren.

2.Objecten hebben als lidvariabelen:als we objecten als lidvariabelen hebben, moeten we de leden van die objecten ook definitief maken. Betekent dat we diep door de boom moeten gaan en alle objecten/primitieven als definitief moeten maken, wat misschien niet altijd mogelijk is. Dus de oplossing is om de klasse definitief te maken, wat overerving voorkomt. Er is dus geen sprake van subklassen die gettermethoden overschrijven.


Antwoord 14

@Value-annotatie van Lombok kan worden gebruikt om onveranderlijke klassen te genereren. Het is zo simpel als de onderstaande code.

@Value
public class LombokImmutable {
    int id;
    String name;
}

Volgens documentatie op de site van Lombok:

@Value is de onveranderlijke variant van @Data; alle velden worden standaard privé en definitief gemaakt en er worden geen setters gegenereerd. De klasse zelf wordt ook standaard definitief gemaakt, omdat onveranderlijkheid niet iets is dat aan een subklasse kan worden opgedrongen. Net als @Data worden ook handige toString(), equals() en hashCode()-methoden gegenereerd, krijgt elk veld een getter-methode en wordt ook een constructor gegenereerd die elk argument omvat (behalve de laatste velden die worden geïnitialiseerd in de velddeclaratie) .

Een volledig werkend voorbeeld vindt u hier.

Other episodes