Nihai nesne neden değiştirilebilir?


90

Üzerinde çalıştığım bir kod tabanında aşağıdaki kodla karşılaştım:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }

    public static void addProvider(ConfigurationProvider provider) {
        INSTANCE.providers.add(provider);
    }

    ...

INSTANCEolarak ilan edilir final. Nesneler neden eklenebilir INSTANCE? Bu, final kullanımını geçersiz kılmamalı. (Olmaz).

Cevabın işaretçiler ve hafıza ile bir şeyler yapması gerektiğini varsayıyorum, ancak kesin olarak bilmek istiyorum.


Bu yanılgı, bir soru olarak değil, oldukça sık ortaya çıkar. Genellikle bir cevap veya yorum olarak.
Robin

5
JLS'den basit açıklama: "Son değişken bir nesneye bir referans içeriyorsa , o zaman nesnenin durumu nesne üzerindeki işlemlerle değiştirilebilir, ancak değişken her zaman aynı nesneye başvurur." JLS Belgeleri
realPK

Yanıtlar:


163

finalbasitçe nesne referansını değiştirilemez hale getirir . İşaret ettiği nesne bunu yaparak değişmez değildir. INSTANCEbaşka bir nesneye asla atıfta bulunamaz, ancak başvurduğu nesnenin durumu değişebilir.


1
+1, Daha fazla ayrıntı için lütfen java.sun.com/docs/books/jls/second_edition/html/… , bölüm 4.5.4'e bakın.
Abel Morelos

Diyelim ki bir ConfigurationServicenesneyi seri halinden çıkardım ve bir ANLIK yapmaya çalışıyorum = deserializedConfigurationServiceizin verilmez mi?
diegoaguilar

Asla INSTANCEbaşka bir nesneye atıfta bulunamazsınız . Diğer nesnenin nereden geldiği önemli değil. (Not: Bu sınıfı yükleyen INSTANCEkişi ClassLoadervardır. Teoride sınıfı bir JVM'ye birkaç kez yükleyebilirsiniz ve her biri ayrıdır. Ancak bu farklı, teknik bir noktadır.)
Sean Owen

@AkhilGite cevabıma yaptığınız düzenleme yanlış yaptı; aslında doğru olan cümlenin anlamını tersine çevirdi. Referans değişmezdir. Nesne değişebilir kalır. "Değişmez hale gelmez".
Sean Owen

@SeanOwen Sry yaptığım düzenleme için, ifadeniz tamamen doğru ve teşekkürler.
AkhilGite

33

Nihai olmak, değişmez olmakla aynı şey değildir.

final != immutable

finalAnahtar emin referans değişmez hale getirmek için kullanılır (olduğu, yeni bir tanesi ile ikame edilen edilemez olan referans)

Ancak, eğer öznitelik değiştirilebilirse, az önce tarif ettiğiniz şeyi yapmanızda bir sakınca yoktur.

Örneğin

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}

Bu sınıfı somutlaştırırsak someFinalObject, son niteliğe sahip olduğu için özniteliğe başka bir değer atayamayız. .

Yani bu mümkün değil:

....
SomeHighLevelClass someObject = new SomeHighLevelClass();
MutableObject impostor  = new MutableObject();
someObject.someFinal = impostor; // not allowed because someFinal is .. well final

Ama eğer nesne kendiliğinden böyle değiştirilebilirse:

class MutableObject {
     private int n = 0;

     public void incrementNumber() {
         n++;
     }
     public String toString(){
         return ""+n;
     }
}  

Daha sonra, bu değiştirilebilir nesnenin içerdiği değer değiştirilebilir.

SomeHighLevelClass someObject = new SomeHighLevelClass();

someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();

System.out.println( someObject.someFinal ); // prints 3

Bu, gönderinizle aynı etkiye sahiptir:

public static void addProvider(ConfigurationProvider provider) {
    INSTANCE.providers.add(provider);
}

Burada INSTANCE'ın değerini değiştirmiyorsunuz, dahili durumunu değiştiriyorsunuz (provider.add yöntemi aracılığıyla)

Sınıf tanımının şu şekilde değiştirilmesini önlemek istiyorsanız:

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }
    // Avoid modifications      
    //public static void addProvider(ConfigurationProvider provider) {
    //    INSTANCE.providers.add(provider);
    //}
    // No mutators allowed anymore :) 
....

Ama pek mantıklı gelmeyebilir :)

Bu arada, temelde aynı nedenden ötürü erişimi de senkronize etmeniz gerekiyor .


26

Yanlış anlaşılmanın anahtarı, sorunuzun başlığındadır. Nihai olan nesne değil , değişkendir . Değişkenin değeri değişemez, ancak içindeki veriler değişebilir.

Bir başvuru türü değişkeni bildirdiğinizde, bu değişkenin değerinin bir nesne değil, bir başvuru olduğunu daima unutmayın.


11

final sadece referansın değiştirilemeyeceği anlamına gelir. Son olarak ilan edilirse INSTANCE'i başka bir referansa yeniden atayamazsınız. Nesnenin iç durumu hala değiştirilebilir.

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

derleme hatası verir


7

Birkez final değişken atandığında, her zaman aynı değeri içerir. Bir finaldeğişken bir nesneye bir referans içeriyorsa, o zaman nesnenin durumu, nesne üzerindeki işlemlerle değiştirilebilir, ancak değişken her zaman aynı nesneyi ifade eder. Bu aynı zamanda diziler için de geçerlidir, çünkü diziler nesnelerdir; bir finaldeğişken bir diziye bir başvuru içeriyorsa, dizinin bileşenleri dizi üzerindeki işlemlerle değiştirilebilir, ancak değişken her zaman aynı diziyi ifade eder.

Kaynak

İşte bir nesneyi değişmez kılmak için bir rehber .


4

Nihai ve değişmez aynı şey değildir. Son, referansın yeniden atanamayacağı anlamına gelir, bu nedenle söyleyemezsiniz

INSTANCE = ...

Değiştirilemez, nesnenin kendisinin değiştirilemeyeceği anlamına gelir. Buna bir örnek java.lang.Stringsınıftır. Bir dizenin değerini değiştiremezsiniz.


2

Java, dilde yerleşik bir değişmezlik kavramına sahip değildir. Yöntemleri bir mutatör olarak işaretlemenin bir yolu yoktur. Bu nedenle dilin nesne değişmezliğini zorlama yolu yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.