Java'da neden korumalı statik kullanmamalıyız?


122

Şu soruyla uğraşıyordum Java'da sınıf değişkenlerini geçersiz kılmanın bir yolu var mı? 36 olumlu oy içeren ilk yorum şuydu:

Eğer bir görürsen protected static, koş.

Biri neden protected statickaşlarını çattığını açıklayabilir mi?


6
Korunan bir statik alanda olduğu sürece yanlış bir şey yoktur final. Sınıflar arasında paylaşılan değişken bir statik alan kesinlikle endişelenmenize neden olur. Statik bir alanı güncelleyen birden fazla sınıf, özellikle herhangi bir korumalı alan veya yöntemin varlığı, sınıfın diğer paketlerdeki sınıflar tarafından genişletilmesi gerektiğini ima ettiğinden, muhtemelen güvenilir veya takip edilmesi kolay değildir. korumalı alanı içeren sınıfın yazarı.
VGR

6
@VGR, finalalanın değişmez olduğu anlamına gelmez. objectBir finalreferans değişkeninin referansını her zaman değiştirebilirsiniz .
Zeeshan

@VGR Katılmıyorum. Statik bir değişken oluşturmanın YALNIZCA nedeni, ona başka bir paket içinden miras yoluyla erişim sağlamaktır ve tek bir alana erişim kalıtım nedeni OLMAMALIDIR. Kusurlu bir tasarım, IMO ve buna başvurursanız, muhtemelen uygulamanızın yapısını yeniden düşünmelisiniz. Bu sadece benim fikrim.
Dioxin

@LoneRider Haklısın. Değişmez düşünüyordum ve son kesinlikle bunu garanti etmiyor.
VGR

Ben bile buraya aynı sorudan geldim.
Raj Rajeshwar Singh Rathore

Yanıtlar:


86

Doğrudan bir sorundan çok biçimsel bir şey. Sınıfta neler olup bittiğini tam olarak düşünmediğinizi gösteriyor.

Ne staticanlama geldiğini bir düşünün :

Bu değişken sınıf düzeyinde mevcuttur, her durum için ayrı ayrı bulunmaz ve beni genişleten sınıflarda bağımsız bir varlığı yoktur .

Ne protectedanlama geldiğini bir düşünün :

Bu değişken bu sınıf, aynı paketteki sınıflar ve beni genişleten sınıflar tarafından görülebilir .

İki anlam tam olarak birbirini dışlamaz, ancak oldukça yakındır.

İkisini birlikte nerede kullanabileceğinizi görebildiğim tek durum, uzatılmak üzere tasarlanmış soyut bir sınıfa sahipseniz ve genişleyen sınıf, orijinalde tanımlanan sabitleri kullanarak davranışı değiştirebiliyorsa. Bu tür bir düzenleme büyük olasılıkla çok dağınık olur ve sınıfların tasarımında zayıflığa işaret eder.

Çoğu durumda sabitlerin herkese açık olması daha iyidir çünkü bu her şeyi daha temiz hale getirir ve insanların alt sınıflara daha fazla esneklik kazandırmasına olanak tanır. Çoğu durumda diğer her şeyden ayrı olarak, kompozisyon kalıtıma tercih edilirken, soyut sınıflar kalıtımı zorlar.

Bunun bir şeyleri nasıl bozabileceğine dair bir örnek görmek ve bağımsız bir varlığı olmayan değişkenle ne demek istediğimi göstermek için şu örnek kodu deneyin:

public class Program {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(new Test2().getTest());
        Test.test = "changed";
        System.out.println(new Test2().getTest());
    }
}

abstract class Test {
    protected static String test = "test";
}

class Test2 extends Test {
    public String getTest() {
        return test;
    }
}

Sonuçları göreceksiniz:

test
changed

Kendiniz deneyin: https://ideone.com/KM8u8O

Sınıf Test2statik üyesi erişebilir testgelen Testismi nitelemek gerek kalmadan - ama devralır değil ya da kendi kopyasını almak. Hafızadaki aynı nesneye bakıyor.


4
Miras konusunda takıntılısınız. Bunun görüldüğü tipik durum, birisinin bunu halka açık hale getirmeden paket erişimini istemesidir (örneğin bir birim testi).
spudone

3
@spudone ancak birim testleri genellikle aynı pakete yerleştirilir. Onlara erişim sağlamak için varsayılan (paket) erişim seviyesini kullanmanız yeterlidir. Korumalı, gerekli olmayan veya birim testi ile ilgili olmayan alt sınıflara da erişim sağlar.
Tim B

1
@Kamafeather Korumalı, çocukların sizi farklı sınıflardan görebileceği ve aynı paketteki herhangi bir sınıfın sizi görebileceği anlamına gelir. Yani evet Program aynı pakette ve dolayısıyla korumalı üyeyi görebilir.
Tim B

2
" genişleyen sınıf daha sonra davranışı değiştirebilir " Bu, Liskov Subsitution ilkesini ihlal eder. Bir alt sınıf, üst türünün davranışını değiştirmemelidir. Bu, tüm "kare bir dikdörtgen değildir" argümanının kapsamına girer: RectangleEşitliği sağlamak için genişliği / yüksekliği ayarlamak için süper sınıfı ( ) ayarlamak, her süper Squaretürü alt türünün bir örneğiyle değiştirirseniz istenmeyen sonuçlar üretecektir.
Dioksin

1
" Bu ikisini birlikte nerede kullanabileceğinizi görebildiğim tek durum , uzatılmak üzere tasarlanmış soyut bir sınıfınız olması ve genişletme sınıfının orijinalde tanımlanan sabitleri kullanarak davranışı değiştirebilmesidir " Biraz gizli, ancak Orada. Kod örneği ayrıca şu ifadeyi de ifade eder
Dioxin

32

Çatışıyor çünkü çelişkili.

Bir değişken yapmak protected, onun paket içinde kullanılacağını veya bir alt sınıf içinde miras alınacağını ima eder .

Değişkeni staticsınıfın bir üyesi yapmak, onu devralma niyetlerini ortadan kaldırır . Bu, yalnızca bir paket içinde kullanılma niyetini bırakır ve buna sahibiz package-private(değiştirici yok).

Eğer JavaFX en gibi uygulamayı (başlatmak için kullanılması gereken bir sınıf bildirerek olsaydı ben için bu yararlı bulabildiğim tek durumdur Application#launchve sadece alt sınıf fırlatıldıktan edebilmek istedim. Bunu yaparken ise, yöntem ayrıca sağlamak finaliçin gizlemeye izin verme . Ancak bu "norm" değildir ve muhtemelen uygulamaları başlatmak için yeni bir yol ekleyerek daha fazla karmaşıklık eklemeyi önlemek için uygulanmıştır.

Her değiştiricinin erişim düzeylerini görmek için, şuna bakın: Java Eğitimleri - Bir Sınıfın Üyelerine Erişimi Denetleme


4
Onu staticmiras alma niyetini nasıl ortadan kaldıracağımı anlamıyorum . Çünkü başka bir paketteki alt sınıfım, protectederişim için süper alana ihtiyaç duyuyor olsa bile static. package-privateyardım edemem
Aobo Yang

@AoboYang Haklısın, bu yüzden bazı insanlar kullanıyor protected static. Ama bu bir kod kokusu, dolayısıyla " çalıştırma " kısmı. Erişim değiştiriciler ve kalıtım iki farklı konudur. Evet, eğer öyleyse süper sınıftan statik bir üyeye erişemezsiniz package-private. Ancak staticilk olarak alanlara başvurmak için mirasa güvenmemelisiniz ; bu kötü tasarımın bir işaretidir. staticYöntemleri geçersiz kılma girişimlerinin sonuç vermediğini fark edeceksiniz , bu da kalıtımın sınıf tabanlı olmadığının açık bir işaretidir. Sınıfın veya paketin dışında erişime ihtiyacınız varsa, bupublic
Dioxin

3
private staticİlk başta bazı kullanım işlevlerine sahip bir sınıfım var , ancak birinin sınıfımdan geliştirmek veya özelleştirmek isteyebileceğini ve bu kullanım işlevlerinin onlara kolaylık sağlayabileceğini düşünüyorum. publicKullanım yöntemleri, sınıfımın örneklerinin kullanıcısı için olmadığından uygun olmayabilir. Bunun yerine iyi bir tasarım bulmama yardım eder misin protected? Teşekkürler
Aobo Yang

Bu bağlantıda, 'Belirli bir üye için anlamlı olan en kısıtlayıcı erişim düzeyini kullan' yazıyor. Yapmamak için iyi bir nedeniniz yoksa özel kullanın. ' , bu soruyla çelişen, herhangi bir düşünceniz var mı?
Cecilia

13

Bunun neden göz ardı edilmesi için belirli bir neden göremiyorum. Her zaman aynı davranışı elde etmek için alternatifler olabilir ve bu alternatiflerin korumalı bir statik yöntemden "daha iyi" olup olmadığına bağlı olarak gerçek mimariye bağlı olacaktır. Ancak, korunan bir statik yöntemin en azından makul olacağı bir örnek şu olabilir:

(Daha protectednet kullanımı sağlamak için ayrı paketlere bölünecek şekilde düzenlenmiştir )

package a;
import java.util.List;

public abstract class BaseClass
{
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeDefaultB(list);
    }

    protected static Integer computeDefaultA(List<Integer> list)
    {
        return 12;
    }
    protected static Integer computeDefaultB(List<Integer> list)
    {
        return 34;
    }
}

Bundan türetilmiştir:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassA extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeOwnB(list);
    }

    private static Integer computeOwnB(List<Integer> list)
    {
        return 56;
    }
}

Başka bir türetilmiş sınıf:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassB extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeOwnA(list)+computeDefaultB(list);
    }

    private static Integer computeOwnA(List<Integer> list)
    {
        return 78;
    }
}

protected staticDeğiştirici kesinlikle burada haklı görülebilir:

  • Yöntemler, staticörnek değişkenlerine bağlı olmadıkları için olabilir . Doğrudan bir polimorfik yöntem olarak kullanılmaları amaçlanmamıştır, bunun yerine, daha karmaşık bir hesaplamanın parçası olan ve gerçek uygulamanın "yapı taşları" olarak hizmet eden varsayılan uygulamaları sunan "yardımcı" yöntemlerdir .
  • Yöntemler publicbir uygulama detayı olduğu için olmamalıdır . Ve olamazlar privateçünkü genişleyen sınıflar tarafından çağrılmaları gerekir. Ayrıca "varsayılan" görünürlüğe sahip olamazlar, çünkü bu durumda diğer paketlerdeki genişletme sınıfları için erişilebilir olmayacaklardır.

(DÜZENLEME: Orijinal yorumun sadece alanlara atıfta bulunduğu ve yöntemlere atıfta bulunmadığı varsayılabilir - o zaman, ancak, çok geneldi)


Bu durumda, varsayılan uygulamaları protected finaldeğil (geçersiz kılınmasını istemediğiniz için) olarak tanımlamalısınız static. Bir yöntem, örnek değişkenler kullanmaz aslında olması gerektiği anlamına gelmez static(bu her ne kadar olabilir ).
barfuin

2
@Thomas Elbette, bu durumda bu da mümkün olabilir. Genel olarak: Bu kesinlikle kısmen özneldir, ancak benim genel kuralım şudur: Bir yöntemin polimorfik olarak kullanılması amaçlanmadığında ve statik olabildiğinde, onu statik hale getiririm. Bunu yapmanın aksine final, yalnızca yöntemin geçersiz kılınmasının amaçlanmadığını açıklığa kavuşturmakla kalmaz, ayrıca okuyucuya yöntemin örnek değişkenleri kullanmadığını da açıklar. Kısaca: Onu statik yapmamak için hiçbir neden yok .
Marco13

1
Bir yöntemin polimorfik olarak kullanılması amaçlanmadığında ve statik olabildiğinde, onu statik hale getiririm. - Bu, birim testi için sahte çerçeveler kullanmaya başladığınızda başınızı belaya sokar. Ama bu bizi farklı bir konuya
götürüyor

@ Marco13 ayrıca bir şey yaptığınızda protected, mirası göstermek için değil, onu tek bir paket altında tutmak için - açıkta değil. Sanırım mühürlü arayüzler
Eugene

@Eugene Bu yorum beni biraz rahatsız ediyor. Paket görünürlüğü herhangi bir değiştirici olmadan elde edilebilir, halbuki protected"Sınıfları devralma (farklı paketlerde bile)" anlamına gelir ...
Marco13

7

Statik üyeler miras alınmaz ve korumalı üyeler yalnızca alt sınıflar (ve tabii ki içeren sınıf) tarafından protected staticgörülebilir , bu nedenle a statickodlayıcı tarafından bir yanlış anlaşılmayı düşündüren ile aynı görünürlüğe sahiptir .


1
Lütfen ilk ifadeniz için bir kaynak sağlar mısınız? Hızlı bir testte, korumalı bir statik int bir alt sınıfa miras alındı ​​ve sorunsuz bir şekilde bir alt sınıfta yeniden kullanıldı.
hiergiltdiestfu

Korumalı statik, özel statik değil, paket özel statik ile aynı görünürlüğe sahiptir. Buna eklemek için, korumalı ve statik kullanıyorsanız, erişim değiştiriciyi pakete özel hale getirmek için kaldırmak en iyisidir (niyetiniz paket içinde erişilebilir
kılmaksa

2
Hayır. özel paket ve protectedaynı değildir. Sadece derseniz static, alan yalnızca aynı paketteki alt sınıflar tarafından görülebilir.
Aaron Digulla

1
@AaronDigulla , paket içinde veya bir alt sınıftan erişime protected staticizin verir . Değişken statik yapmak, alt sınıflandırma niyetlerini ortadan kaldırır ve tek niyetin paketin içinden erişim olmasını sağlar.
Dioxin

@VinceEmigh: Üzgünüm, Bohemian ile konuşuyordum. Bunu daha açık hale getirmeliydim.
Aaron Digulla

5

Aslında temelde yanlış olan hiçbir şey yok protected static. Paket ve bildiren sınıfın tüm alt sınıfları için görünür olan bir statik değişken veya yöntemi gerçekten istiyorsanız, devam edin ve yapınprotected static .

Bazı insanlar genellikle kullanmayı önlemek protectedçeşitli nedenlerle ve bazı insanlar olmayan nihai düşünüyorum staticben kombinasyonunu tahmin böylece değişkenler, (Ben şahsen bir dereceye kadar ikincisi sempati) elbette kaçınılmalıdır protectedve staticbakmalıyız ^ 2 kötü olanlar buna her iki gruba da aittir.


3

Alt sınıflarda kullanılabilmesi için korumalı kullanılır. Aynı değişkene statik bir şekilde erişebileceğinizden, somut sınıflar bağlamında kullanırken korumalı bir statik tanımlamada mantık yoktur, ancak derleyici, süper sınıf statik değişkenine statik bir şekilde erişmek için bir uyarı verecektir.


1

Pek çok insanın cevapladığı gibi:

  • protectedanlamı - ' özel paket + alt sınıflara görünürlük - özellik / davranış KIRILMIŞTIR '
  • staticanlamı - ' örneğin tersi - bir SINIF özelliği / davranışıdır, yani Kalıtımsal DEĞİLDİR '

Bu nedenle biraz çelişkili ve uyumsuzdurlar.

Ancak son zamanlarda bu ikisini birlikte kullanmanın mantıklı olabileceği bir kullanım durumu buldum. Değişmez tipler abstractiçin bir ebeveyn olan ve alt tiplerde ortak olan bir sürü özelliği olan bir sınıf yaratmak istediğinizi hayal edin . Değişmezliği doğru bir şekilde uygulamak ve okunabilirliği korumak için Oluşturucu modelini kullanmaya karar verilebilir .

package X;
public abstract class AbstractType {
    protected Object field1;
    protected Object field2;
    ...
    protected Object fieldN;

    protected static abstract class BaseBuilder<T extends BaseBuilder<T>> {
        private Object field1; // = some default value here
        private Object field2; // = some default value here
        ...
        private Object fieldN; // = some default value here

        public T field1(Object value) { this.field1 = value; return self();}
        public T field2(Object value) { this.field2 = value; return self();}
        ...
        public T fieldN(Object value) { this.fieldN = value; return self();}
        protected abstract T self(); // should always return this;
        public abstract AbstractType build();
    }

    private AbstractType(BaseBuilder<?> b) {
        this.field1 = b.field1;
        this.field2 = b.field2;
        ...
        this.fieldN = b.fieldN;
    }
}

Ve neden protected static? Ben olmayan bir soyut alt türü istiyoruz AbstactTypekendi olmayan soyut Builder uygulayan ve dış bulunduğu package Xerişebileceğini ve yeniden kullanmak BaseBuilder.

package Y;
public MyType1 extends AbstractType {
    private Object filedN1;

    public static class Builder extends AbstractType.BaseBuilder<Builder> {
        private Object fieldN1; // = some default value here

        public Builder fieldN1(Object value) { this.fieldN1 = value; return self();}
        @Override protected Builder self() { return this; }
        @Override public MyType build() { return new MyType(this); }
    }

    private MyType(Builder b) {
        super(b);
        this.fieldN1 = b.fieldN1;
    }
}

Elbette BaseBuilderkamuoyuna duyurabiliriz ama sonra başka bir çelişkili ifadeye geliriz:

  • Örneklenemeyen bir sınıfımız var (soyut)
  • Bunun için bir kamu kurucusu sağlıyoruz

Yani her iki durumda da protected staticve publicbir inşa eden firmaabstract class çelişkili ifadeleri birleştiriyoruz. Kişisel tercihler meselesidir.

Ancak, ben yine de tercih publicbir yapımcısı durumunaabstract class çünkü protected staticbana OOD ve cepten dünyada daha doğal hissediyor!


0

Sahip olmanın yanlış bir tarafı yok protected static. Pek çok insanın gözden kaçırdığı bir şey, normal koşullar altında açığa çıkarmak istemediğiniz statik yöntemler için test senaryoları yazmak isteyebileceğinizdir. Bunun özellikle fayda sınıflarında statik yöntem için testler yazmak için yararlı olduğunu fark ettim.

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.