Java'da neden statik bir iç içe arabirim kullanılır?


235

Ben sadece bizim kod tabanı statik bir iç içe arabirim bulduk.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Bunu daha önce hiç görmedim. Orijinal geliştiriciye erişilemiyor. Bu yüzden SO sormak zorunda:

Statik bir arayüzün ardındaki anlamlar nelerdir? Kaldırırsam ne değişecekti static? Biri bunu neden yapar ki?


2
Bu bir 'iç arabirim değildir: iç içe geçmiş bir arabirimdir. Java'nın içsel bir anlamı vardır.
Lorne Marquis

Yanıtlar:


293

Yukarıdaki örnekteki statik anahtar kelime gereksizdir (iç içe bir arabirim otomatik olarak "statik" tir) ve anlambilimi etkilemeden kaldırılabilir; Kaldırılmasını tavsiye ederim. Aynı şey arabirim yöntemlerinde "genel" ve arabirim alanlarında "genel final" için de geçerlidir - değiştiriciler gereksizdir ve sadece kaynak koduna karmaşa ekler.

Her iki durumda da, geliştirici sadece Foo.Bar adında bir arabirim bildiriyor. Foo'ya erişemeyen kodun Foo.Bar'a da erişemeyeceği dışında, ek sınıfla başka bir ilişki yoktur. (Kaynak kodundan - bayt kodu veya yansıma Foo paket özel olsa bile Foo.Bar'a erişebilir!)

Yeni bir üst düzey ad oluşturmamanız için, yalnızca dış sınıftan kullanılmasını beklerseniz bu şekilde iç içe bir arabirim oluşturmak kabul edilebilir bir stildir. Örneğin:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});

1
Jesse Glick'in cevabında bunun anlamı: (Kaynak kodundan - bayt kodundan veya yansımadan Foo paket özel olsa bile Foo.Bar'a erişebilir!).
Vasu

Kaillash, özel yöntemlere relfection (yansıtma paketinde) ve oluşturulan .class dosyalarının bayt koduna doğrudan erişerek erişilebilir.
gmoore

2
"Bytecode ... Foo.Bar erişebilirsiniz" Yani Foo.Bar başvuru derlenmiş bir sınıf Foo başvuru olmasa bile yüklenebilir ve çalıştırılabilir. Bu, sınıf Foo'nun herkese açık olduğu bir zamanda daha önce derlenmişse veya sınıf, Java dışındaki bir dilde vb. Elle bir araya getirilmiş veya derlenmişse oluşabilir. Ancak Java derleyicisi, ek sınıftaki erişim değiştiriciyi denetler. sonuçta elde edilen bayt kodu bu kapalı sınıfa atıfta bulunmasa bile.
Jesse Glick

@Jesse Özel bir üst düzey sınıftaki özel bir statik sınıfa yansıma ile erişilebilir mi?
Pacerier

1
@ Pacer: hayır. Daha doğrusu, sınıfı yükleyebilir ve üyelerini inceleyebilirsiniz, ancak setAccessible (true) kullanmadan sınıfını başlatamaz veya yöntem çağıramazsınız. Başka bir deyişle, yansıma yoluyla görünür ancak erişilebilir değildir. Ancak iç içe statik sınıf herkese açıksa, statik olarak erişilebilir olmasa da (derleme sırasında) varsayılan olarak yansıma yoluyla erişilebilir.
Jesse Glick

72

Soru cevaplandı, ancak iç içe bir arabirim kullanmanın iyi bir nedeni, işlevinin doğrudan içinde bulunduğu sınıfla ilgili olup olmadığıdır. Bunun iyi bir örneği a Listener. Bir sınıfınız varsa Foove diğer sınıfların olayları dinleyebilmesini istiyorsanız FooListener, tamam adında bir arabirim bildirebilirsiniz, ancak tamam, ancak iç içe bir arabirim bildirmek ve bu diğer sınıfların uygulanmasını sağlamak daha açık olacaktır Foo.Listener( yuvalanmış bir sınıf Foo.Eventbununla birlikte kötü değildir).


11
Bunun tipik bir örneği java.util.Map.Entry(başka bir arayüzde iç içe geçen bir arayüzdür).
Paŭlo Ebermann

4
Bunun eski bir konu olduğunu biliyorum, ama dış sınıfın kendi paketinde olmasını ve herhangi bir ek arabirimin (örneğin Map.Entry) veya sınıfların da bu paket içinde olmasını tercih ederim . Bunu söylüyorum çünkü sınıflarımı kısa ve öz tutmayı seviyorum. Ayrıca bir okuyucu, paketteki sınıflara bakarak diğer varlıkların bir sınıfla ilişkili olduğunu görebilir. Muhtemelen java.collections.mapharitalar için bir paketim olurdu . Bu OO ve modülerlik ile ilgilidir. java.utiliçinde çok fazla şey var. utilgibi common- bir koku IMO
David Kerr

1
@Shaggy: İçinde java.utilkesinlikle çok fazla var. Bununla birlikte, önerdiğiniz gibi ince taneli paketlere ayrılmanın da ideal olduğunu düşünmüyorum.
ColinD

1
@DavidKerr Bu arayüzü java.util.MapEntrykendi paketinin dışında çağırdığınızı varsayın . İlk refleks: bu sınıfla ilgili arayüz nedir? Sınıfa bakıyorum. Javadoc bu arayüze bağlantı vermedikçe, ilişkileri hakkında hiçbir fikrim yok. Ayrıca insanlar içe aktarma paketine bakmaz. Herkesin kendi görüşü vardır. Kimse doğru değil, kimse yanlış değil
Raymond Chenon

Dediğim şeyin bir kısmı da Map ve ilişkili sınıflarını, örneğin Map.Entry'yi ayrı bir pakete koymaktı, örneğin java.collections.map. Bu şekilde, harita ile ilgili tüm kodlar tek bir konumda (paket) olur ve üst düzey Harita sınıfı yüklenmez. Belki de ilgili uygulamaları (HashMap, vb.) Aynı pakete koyardım.
David Kerr

14

Üye arabirimleri dolaylı olarak statiktir. Örneğinizdeki statik değiştirici, kodun anlamını değiştirmeden kaldırılabilir. Ayrıca bkz. Java Dil Spesifikasyonu 8.5.1. Statik Üye Tipi Beyanları


Bu bir 'iç arabirim' değildir: iç içe geçmiş bir arabirimdir. Java'nın içsel bir anlamı vardır.
Lorne Marquis

@EJP, terimleri birbirinin yerine kullanarak çeşitli blogları okudum. İç arayüz var mı? Yuvalanmış arayüzden nasıl farklıdırlar?
Sayı 945

@BreakingBenjamin, İç içe arabirim statik anlamına gelir. İç sınıf ve iç içe sınıf vardır, ancak iç arayüz yoktur. Öyle olsa bile, iç içe arayüz olarak adlandırılmalıdır. İç - statik olmayan ve iç içe geçmiş statiktir.
Sundar Rajan

9

Bir iç arabirime erişilebilmesi için statik olması gerekir. Arabirim sınıfın örnekleriyle değil, sınıfın kendisiyle ilişkilendirilir, dolayısıyla şu şekilde erişilebilir Foo.Bar:

public class Baz implements Foo.Bar {
   ...
}

Çoğu durumda, bu statik bir iç sınıftan farklı değildir.


35
İç içe bir arabirim, anahtar sözcük yazsın veya yazmasın otomatik olarak statiktir.
Paŭlo Ebermann

3
Topluluğun farklı bir cevabı kabul etmek için oy kullanması için gerçekten bir yola ihtiyacımız var: stackoverflow.com/a/74400/632951
Pacerier


@ ClintonN.Dreisbach Daha ne demek istediğini açıklayabilir misin The interface isn't associated with instances of the class, but with the class itself, anlamadım
Kasun Siyambalapitiya

6

Jesse'nin cevabı yakın, ama iç arayüzün neden faydalı olabileceğini göstermek için daha iyi bir kod olduğunu düşünüyorum. Okumaya başlamadan önce aşağıdaki koda bakın. İç arayüzün neden faydalı olduğunu bulabilir misiniz? Cevap, DoSomethingAlready sınıfı A ve C'yi uygulayan herhangi bir sınıfla başlatılabilir ; sadece beton sınıfı Zoo değil. Tabii ki, AC içsel olmasa bile elde edilebilir, ancak daha uzun isimleri (sadece A ve C değil) birleştirmeyi ve bunu diğer kombinasyonlar için (örneğin, A ve B, C ve B, vb.) Yaptığınızı ve kolayca işlerin nasıl kontrolden çıktığını görün. Kaynak ağacınızı gözden geçiren kişilerin yalnızca bir sınıfta anlamlı olan arayüzler tarafından boğulacağından bahsetmiyoruz bile. iç arabirim , özel türlerin inşa edilmesini sağlar ve kapsüllemelerini geliştirir .

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}

2
Bu saçmalık. Sınıf Zooyok değil arabirimini uygulamak ACnedenle örnekleri, Zookutu değil yapıcı geçirilecek DoSomethingAlreadybekler hangi AC. Aslında AChem uzanır Ave C, sınıflar uygulanması anlamına gelmez Ave Csihirli da uygulamak AC.
Holger


0

Genellikle statik iç sınıflar görüyorum. Statik iç sınıflar, statik olmayan sınıfların yapabileceği kapsayıcı sınıflara başvuramaz. Bazı paket çarpışmalarına girmediğiniz sürece (zaten Foo ile aynı pakette Bar adında bir arayüz var) Sanırım bu dosyayı kendi dosyası yapardım. Foo ve Bar arasındaki mantıksal bağlantıyı uygulamak da bir tasarım kararı olabilir. Belki de yazar Bar'ın sadece Foo ile birlikte kullanılmasını istedi (statik bir iç arayüz bunu zorlamayacak, sadece mantıklı bir bağlantı)


'Statik iç', bir çelişki. İç içe sınıfları ya statik veya iç.
Lorne Marquis

0

Sınıf Foo'yu Foo arayüzüne dönüştürürseniz, yukarıdaki örnekteki "public" anahtar kelimesi de gereksiz olacaktır, çünkü

başka bir arayüzde tanımlanan arayüz dolaylı olarak halka açık statik olacaktır .


Soruya cevap vermedi
yağmur yağıyor

0

1998'de Philip Wadler statik arayüzler ile statik olmayan arayüzler arasında bir fark önermiştir.

Görebildiğim kadarıyla, bir arabirimi statik olmayan hale getirmedeki tek fark, artık statik olmayan iç sınıfları içerebilmesidir; bu nedenle değişiklik mevcut Java programlarını geçersiz hale getirmez.

Örneğin , bir yandan “diliniz ne kadar ifade edebilir” ifadesi, diğer yandan “kendi dilinizde temsil etmeye çalıştığınız terimler” ifadesi arasındaki uyumsuzluk olan İfade Problemine bir çözüm önerdi. .

Statik ve statik olmayan yuvalanmış arabirimler arasındaki farkın bir örneği, örnek kodunda görülebilir :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Onun önerisi asla Java 1.5.0'da yapılmadı. Bu nedenle, diğer tüm cevaplar doğrudur: statik ve statik olmayan iç içe arabirimler arasında bir fark yoktur.


Tüm bunların, Java'daki jenerikler için çok erken bir işlemci olan GJ'ye atıfta bulunduğuna dikkat edilmelidir.
Lorne Marquis

-1

Java'da statik arabirim / sınıf, arabirimin / sınıfın üst düzey bir sınıf gibi kullanılmasına izin verir, yani diğer sınıflar tarafından bildirilebilir. Böylece şunları yapabilirsiniz:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Statik olmadan, yukarıdaki derleme başarısız olur. Bunun avantajı, sadece arayüzü bildirmek için yeni bir kaynak dosyasına ihtiyacınız olmamasıdır. Ayrıca Foo.Bar yazmanız gerektiğinden Bar arayüzünü görsel olarak Foo sınıfıyla ilişkilendirir ve Foo sınıfının Foo.Bar örnekleri ile bir şey yaptığını ima eder.

Java'daki sınıf türlerinin açıklaması .


Statik olmadan yapar derleme. 'Statik' gereksizdir.
Lorne Marquis

@EJP: Bağlantı kurduğum makaleye göre, statik olmadan iç sınıf borçlu sınıfın dışındaki herhangi bir şey tarafından değil, yalnızca sahip olan sınıf tarafından kullanılabilir. Statik onu iç içe bir üst düzey sınıf yapar ve sahip olan sınıfın kapsamı dışındaki nesneler tarafından kullanılabilir (en azından makaleye göre). Statik tamamen gereksiz değildir ve iç sınıfın kapsamını etkiler. Bu sınıflar için geçerlidir, arayüzler her zaman üst düzey nesneler gibi davranabilir (kontrol etmek için dil özelliklerini okumalısınız). Belki de cevabımın arayüzünü ve sınıf kısımlarını (kötü) ayırmalıydım.
Skizz

-6

Statik, paketin (projenin) herhangi bir sınıf bölümünün bir işaretçi kullanmadan erişebileceği anlamına gelir. Bu duruma bağlı olarak yararlı veya engelleyici olabilir.

"Statik" yöntemlerin yararlı örneklerine Math sınıfı verilebilir. Math'daki tüm yöntemler statiktir. Bu, yoldan çıkmak, yeni bir örnek yapmak, değişkenleri bildirmek ve daha da fazla değişkene depolamak zorunda olmadığınız anlamına gelir, sadece verilerinizi girebilir ve sonuç alabilirsiniz.

Statik her zaman işe yaramaz. Örneğin büyük / küçük harf karşılaştırması yapıyorsanız, verileri birkaç farklı şekilde depolamak isteyebilirsiniz. Aynı imzalarla üç statik yöntem oluşturamazsınız. Statik olmayan 3 farklı örneğe ihtiyacınız var ve statik ise, veriler girişle birlikte değişmeyecektir.

Statik yöntemler, bir kerelik geri dönüşler ve hızlı hesaplamalar veya kolay elde edilen veriler için iyidir.


1
Statik ne anlama geldiğini biliyorum. Daha önce hiç bir arayüzde görmemiştim. Bu nedenle, sorunuz konu dışı - özür dilerim
Pzt

1
Cevabının konu dışı olduğunu düşünüyorum.
Koray Tugay
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.