Java neden iç sınıflarda statik alanları yasaklıyor?


85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

Statik alana erişim mümkün olmasa da, statik OuterClass.InnerClass.iolması gereken bir şeyi, örneğin oluşturulan InnerClass nesnelerinin sayısını kaydetmek istersem, bu alanı statik yapmak faydalı olacaktır. Öyleyse Java neden iç sınıflarda statik alanları / yöntemleri yasaklıyor?

DÜZENLEME: Derleyiciyi statik iç içe sınıfla (veya statik iç sınıfla) nasıl mutlu edeceğimi biliyorum, ancak bilmek istediğim şey java'nın neden hem dil tasarımından hem de iç sınıflardaki (veya sıradan iç sınıf) statik alanları / yöntemleri yasakladığı Birisi hakkında daha fazla şey biliyorsa, uygulama yönleri.


3
En sevdiğim örnek, sadece iç sınıf için bir Logger'a sahip olmak. Diğer tüm Kaydediciler gibi statik olamaz.
Piotr Findeisen

Java 16'dan beri artık durum böyle değil - bu yanıta bakın .
Nicolai Parlog

Yanıtlar:


32

İç sınıfların arkasındaki fikir, çevreleyen örnek bağlamında çalışmaktır. Her nasılsa, statik değişkenlere ve yöntemlere izin vermek bu motivasyonla çelişiyor mu?

8.1.2 İç Sınıflar ve Çevreleyen Eşgörünümler

İç sınıf, açıkça veya örtük olarak statik olarak bildirilmemiş iç içe geçmiş bir sınıftır. İç sınıflar, statik başlatıcıları (§8.7) veya üye arabirimleri bildiremez. İç sınıflar, derleme zamanı sabit alanları olmadıkça (§15.28) statik üyeler bildiremez.


19
belki sadece oluyor karar böyle
Gregory Pakosz

3
Eğer olamaz örneğini bir üst referans olmadan statik olmayan iç, ama yine de olabilir başlatmak onu.
skaffman

ClassLoader'lar "Class X başlatıldı" yazan bir önbellek tutarsa, bu durumda mantıkları Class [nesneleri temsil eden] X'in birden çok örneğini başlatmak için kullanılamaz (bu, Class nesnelerinin birkaç iç sınıfta iç sınıflar olarak somutlaştırılması gerektiğinde gerekli olan şeydir) farklı nesneler).
Erwin Smout

@skaffman Hala mantıklı değil. İç sınıfın statik özellikleri yalnızca bir kez başlatılacak, öyleyse sorun ne olacak? Şu anda, statik bir hashmap'im var ve yalnızca bu haritayı işleyen yaklaşık 4 yöntemim var, bu da her şeyi bir iç sınıfta gruplamayı daha ideal hale getiriyor. Bununla birlikte, statik hashmap artık dışarıda yaşamak zorunda kalacak ve muhtemelen diğer şeylerle ilgili, bu sadece aptalca. Statik özelliklerin başlatılmasıyla ilgili sorun ne olabilir?
mmm

Java 16'dan beri artık durum böyle değil - bu yanıta bakın .
Nicolai Parlog

56

bilmek istediğim şey, java'nın neden iç sınıflar içindeki statik alanları / yöntemleri yasakladığı

Çünkü bu iç sınıflar "örnek" iç sınıflardır. Yani, çevreleyen nesnenin bir örnek niteliği gibidirler.

"Örnek" sınıfları olduklarından, staticözelliklere izin vermenin bir staticanlamı yoktur, çünkü ilk etapta bir örnek olmadan çalışmak içindir.

Aynı anda bir statik / örnek özniteliği oluşturmaya çalışırsınız.

Aşağıdaki örneği ele alalım:

class Employee {
    public String name;
}

İki çalışan örneği oluşturursanız:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

Neden her birinin mülk için kendi değeri olduğu açık name, değil mi?

Aynı şey iç sınıf için de olur; her bir iç sınıf örneği, diğer iç sınıf örneğinden bağımsızdır.

Dolayısıyla, bir countersınıf özelliği oluşturmaya çalışırsanız , bu değeri iki farklı örnek arasında paylaşmanın bir yolu yoktur.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

Örneği oluşturduğunuzda ave byukarıdaki örnekte, statik değişken için doğru değer ne olur count? Bunu belirlemek mümkün değildir, çünkü InnerDatasınıfın varlığı tamamen çevreleyen nesnelerin her birine bağlıdır.

Bu nedenle, sınıf ilan edildiğinde static, kendini yaşamak için artık bir canlı örneğe ihtiyaç duymuyor. Artık bağımlılık olmadığına göre, statik bir özniteliği özgürce bildirebilirsiniz.

Bunun kulağa tekrarlayıcı geldiğini düşünüyorum, ancak örnek ile sınıf nitelikleri arasındaki farkları düşünürseniz, mantıklı olacaktır.


4
İç sınıfın statik özellikleri için açıklamanızı satın alacağım , ancak @skaffman'ın cevabıma yaptığı bir yorumda işaret ettiği gibi, peki ya statik yöntemler ? Görünüşe göre yöntemlere herhangi bir örnekten ayrılmalarını zorunlu kılmaksızın izin verilmelidir. Aslında, java'da örneklerde statik yöntemler çağırabilirsiniz (kötü stil olarak kabul edilmesine rağmen). BTW: C # olarak OP'ın kodunu derlemek girişimi için iş arkadaşlarınızdan istedi ve does derleme. Dolayısıyla C #, OP'nin yapmak istediği şeyin bazı temel OO ilkelerini ihlal etmediğini gösteren buna izin veriyor.
Asaph

2
Yöntemlerle tamamen aynı olur. Buradaki mesele, özniteliklerin veya yöntemlerin statis olup olmadığı değil, iç sınıfın kendisinin örnek "malzeme" olmasıdır. Demek istediğim, buradaki sorun, böyle bir iç sınıfın dış sınıf yaratılıncaya kadar var olmamasıdır. Dolayısıyla, hiçbir şey yoksa, o zaman hangi yöntem gönderilir. Sadece ilk etapta bir örneğe ihtiyacınız olacağı için havaya vurursunuz.
OscarRyz

1
C # hakkında ... pekala. Sadece C # izin verdiği için OO geçerli değildir, yanlış olduğunu kastetmiyorum, ancak C #, tutarlılık pahasına bile geliştirmeyi kolaylaştırmak için birkaç paradigma içerir (her .NET sürümüyle yeni şeyler öğrenmeniz gerekir) ve buna ve diğer türlere diğerlerinin yanı sıra izin verir. Bunun iyi bir şey olduğunu düşünüyorum. Topluluk ekstra bir özelliğin yeterince havalı olduğunu hissederse, C # gelecekte buna sahip olabilir.
OscarRyz

2
@OscarRyz Statik yöntemlerini / alanlarını kullanmak için neden iç sınıfın örneklerine ihtiyacınız var ? Ve iç sınıftaki [yararlı] statik yönteme bir örnek , özel yardımcı yöntemdir.
Leonid Semyonov

1
Kullanımıyla final, javada iç sınıfta statik alanlara izin verilir. Bu senaryoyu nasıl açıklıyorsunuz?
Number945

34

InnerClassstaticbir örneğine ait olduğu için üye olamaz OuterClass. Eğer bildirirseniz InnerClassolarak staticörneğinden ayırmak için, kodunuz derleyecek.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

BTW: Yine de örneklerini oluşturabilirsiniz InnerClass. staticbu bağlamda bunun kapalı bir örneği olmadan gerçekleşmesine izin verir OuterClass.


6
InnerClassyok değil aittir OuterClass, örneklerini bunu yapmak. İki sınıfın kendilerinin böyle bir ilişkisi yoktur. Neden statik yöntemlere sahip olamayacağınız sorusu InnerClasshala duruyor.
skaffman

10

Aslında, sabitlerse ve derleme zamanında yazılmışlarsa statik alanları bildirebilirsiniz.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}

8
  1. sınıf Başlatma sırası kritik bir nedendir.

İç sınıflar, çevreleyen / Dış sınıf örneğine bağlı olduğundan, Dış sınıfın, Inner sınıfının başlatılmasından önce başlatılması gerekir.
JLS, sınıf Başlatma hakkında diyor. İhtiyacımız olan nokta, sınıf T'nin başlatılacağı

  • T tarafından bildirilen statik bir alan kullanılır ve alan sabit bir değişken değildir.

Dolayısıyla, iç sınıfın, iç sınıfın başlatılmasına neden olacak statik bir alana erişimi varsa, ancak bu, çevreleyen sınıfın başlatılmasını sağlamaz.

  1. Bazı temel kuralları ihlal ederdi . noob şeylerden kaçınmak için son bölüme (- two cases) geçebilirsiniz

Bir şey hakkında , bazı zaman ise sadece her şekilde normal bir sınıf gibi davranır ve onu dış sınıfla ilişkilidir.static nested classnested classstatic

Ancak Inner class/ mi kavramı, dış / çevreleyen sınıfla ilişkilendirilecektir. Lütfen sınıfla değil örnekle ilişkili olduğunu unutmayın . Şimdi örnekle ilişkilendirmek, açıkça ( örnek değişkeni kavramından ) bir örnek içinde var olacağı ve örnekler arasında farklı olacağı anlamına gelir. non-static nested classinstance

Şimdi, statik bir şey yaptığımızda, sınıf yüklenirken başlatılmasını ve tüm örnekler arasında paylaşılması gerektiğini umuyoruz. Ancak statik olmadıkları için , iç sınıfların kendileri bile ( şimdilik iç sınıfın örneğini kesinlikle unutabilirsiniz ) dış / çevreleyen sınıfın tüm örnekleriyle ( en azından kavramsal olarak ) paylaşılmaz , o zaman bazı değişkenleri nasıl bekleyebiliriz? İç sınıf, iç sınıfın tüm örnekleri arasında paylaşılacaktır.

Öyleyse Java, statik iç içe olmayan sınıf içinde statik değişken kullanmamıza izin veriyorsa. iki durum olacak .

  • İç sınıfın tüm örneğiyle paylaşılırsa, context of instance(örnek değişkeni) kavramını ihlal eder . Bu bir HAYIR o zaman.
  • Tüm örnekle paylaşılmazsa, statik olma kavramını ihlal eder. Yine HAYIR.

5

İşte bu "limit" için en uygun bulduğum motivasyon: Bir iç sınıfın statik alanının davranışını, dış nesnenin bir örnek alanı olarak uygulayabilirsiniz; Yani statik alanlara / yöntemlere ihtiyacınız yoktur . Demek istediğim davranış, bazı nesnelerin tüm iç sınıf örneklerinin bir alanı (veya yöntemi) paylaşmasıdır.

Öyleyse, tüm iç sınıf örneklerini saymak istediğinizi varsayalım, yaparsınız:

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}

2
Ama soru şu ki, o zaman ilan edildiklerinde statik alanlara izin vermenin nedeni finalnedir?
Solace

[ stackoverflow.com/a/1954119/1532220] 'ye bakarsanız (yukarıdaki OscarRyzs cevabı): Onun motivasyonu, bir değerin değişkenle ilişkilendirilememesidir. Elbette, eğer değişken nihai ise, hangi değeri atayacağınızı kolayca bilebilirsiniz (bilmelisiniz).
ianos

2

Basit bir deyişle, statik olmayan iç sınıflar, dış sınıf için örnek değişkenidir ve yalnızca bir dış sınıf oluşturulduğunda ve çalışma zamanında bir dış sınıf nesnesi oluşturulurken, statik değişkenler sınıf yükleme zamanında oluşturulurken oluşturulurlar. Yani statik olmayan iç sınıf çalışma zamanı meselesidir, bu yüzden statik, statik olmayan bir iç sınıfın parçası değildir.

NOT: iç sınıfları her zaman bir dış sınıf için bir değişken olarak ele alın, diğer değişkenler gibi statik veya statik olmayabilir.


Ancak iç sınıfın static finalsabitleri olabilir .
rainning


1

Çünkü "durağan" anlamında belirsizliğe neden olur.

İç sınıflar, derleme zamanı sabitleri dışında statik üyeler bildiremez. "Statik" kelimesinin anlamı konusunda bir belirsizlik olacaktır. Sanal makinede yalnızca bir örnek olduğu anlamına mı geliyor? Veya dış nesne başına yalnızca bir örnek? Dil tasarımcıları bu sorunu çözmemeye karar verdiler.

Cay S. Horstmann tarafından "Sabırsızlar için Core Java SE 9" dan alınmıştır. Syf 90 Bölüm 2.6.3


0

Java 16'dan itibaren artık durum böyle değil. JEP 395'ten alıntı (kayıtların tamamlanması üzerine):

Bir iç sınıfın açıkça veya örtük olarak statik olan bir üye bildiremediği uzun süredir devam eden kısıtlamayı gevşetin. Bu yasal hale gelecek ve özellikle bir iç sınıfın bir kayıt sınıfı olan bir üye ilan etmesine izin verecektir.

Aslında, aşağıdaki kod Java 16 ile derlenebilir (16.ea.27 ile denendi):

public class NestingClasses {

    public class NestedClass {

        static final String CONSTANT = new String(
                "DOES NOT COMPILE WITH JAVA <16");

        static String constant() {
            return CONSTANT;
        }

    }

}

-1

Sanırım tutarlılık için. Bunun için herhangi bir teknik sınırlama yokmuş gibi görünse de, iç sınıfın statik üyelerine dışarıdan erişemezsiniz, yani OuterClass.InnerClass.iorta basamak statik olmadığı için.


Ancak iç sınıfın static finalsabitleri olabilir .
rainning
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.