Mengapa antarmuka bersarang statis digunakan di Jawa?

235

Saya baru saja menemukan antarmuka bersarang statis di basis kode kami.

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

Saya belum pernah melihat ini sebelumnya. Pengembang asli berada di luar jangkauan. Karena itu saya harus bertanya kepada SO:

Apa yang dimaksud dengan semantik di belakang antarmuka statis? Apa yang akan berubah, jika saya menghapus static? Mengapa ada orang yang melakukan hal ini?

Mo.
sumber
2
Ini bukan antarmuka bagian dalam: ini adalah antarmuka bersarang . Batin memiliki makna khusus di Jawa.
Marquis of Lorne

Jawaban:

293

Kata kunci statis dalam contoh di atas adalah redundan (antarmuka bersarang secara otomatis "statis") dan dapat dihapus tanpa efek pada semantik; Saya akan merekomendasikan itu dihapus. Hal yang sama berlaku untuk "publik" pada metode antarmuka dan "final publik" pada bidang antarmuka - pengubah berlebihan dan hanya menambahkan kekacauan pada kode sumber.

Either way, pengembang hanya mendeklarasikan antarmuka bernama Foo.Bar. Tidak ada hubungan lebih lanjut dengan kelas terlampir, kecuali kode yang tidak dapat mengakses Foo tidak akan dapat mengakses Foo.Bar juga. (Dari kode sumber - bytecode atau refleksi dapat mengakses Foo.Bar bahkan jika Foo adalah paket-pribadi!)

Merupakan gaya yang dapat diterima untuk membuat antarmuka bersarang dengan cara ini jika Anda mengharapkannya hanya digunakan dari kelas luar, sehingga Anda tidak membuat nama tingkat atas yang baru. Sebagai contoh:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Jesse Glick
sumber
1
Dalam jawaban Jesse Glick, apa artinya ini: (Dari kode sumber - bytecode atau refleksi dapat mengakses Foo.Bar bahkan jika Foo adalah paket-pribadi!).
Vasu
Kaillash, metode pribadi dapat diakses melalui relfection (dalam paket mencerminkan) dan melalui secara langsung mengakses bytecode dari file .class yang dihasilkan.
gmoore
2
Dengan "bytecode ... dapat mengakses Foo.Bar" Maksud saya kelas terkompilasi yang mereferensikan Foo.Bar dapat dimuat dan dijalankan bahkan jika itu tidak dapat merujuk Foo. Ini mungkin terjadi jika kelas dikompilasi pada waktu yang lebih awal ketika Foo bersifat publik, atau jika kelas tersebut dirakit secara manual atau dikompilasi dari beberapa bahasa non-Jawa, dll. Kompiler Java, bagaimanapun, memeriksa pengubah akses pada kelas yang menyertakan bahkan ketika bytecode yang dihasilkan tidak akan merujuk ke kelas terlampir itu.
Jesse Glick
@Jesse Bisakah kelas statis pribadi di kelas atas pribadi diakses melalui refleksi?
Pacerier
1
@Pacerier: no. Lebih tepatnya, Anda dapat memuat kelas dan memeriksa anggotanya, tetapi tidak dapat membuat instantiate atau memanggil metode di atasnya tanpa menggunakan setAccessible (true). Dengan kata lain itu terlihat, tetapi tidak dapat diakses, melalui refleksi. Tetapi jika kelas statis bersarang adalah publik, itu dapat diakses secara default melalui refleksi meskipun itu tidak dapat diakses secara statis (selama kompilasi).
Jesse Glick
72

Pertanyaan telah dijawab, tetapi salah satu alasan bagus untuk menggunakan antarmuka bersarang adalah jika fungsinya terkait langsung dengan kelas yang ada di dalamnya. Contoh yang bagus untuk ini adalah a Listener. Jika Anda memiliki kelas Foodan Anda ingin kelas lain dapat mendengarkan acara di dalamnya, Anda bisa mendeklarasikan antarmuka bernama FooListener, yang ok, tetapi mungkin akan lebih jelas untuk mendeklarasikan antarmuka bersarang dan meminta kelas-kelas lain menerapkan Foo.Listener( kelas bersarang Foo.Eventtidak buruk seiring dengan ini).

ColinD
sumber
11
Contoh tipikal adalah java.util.Map.Entry(yang merupakan antarmuka bersarang di antarmuka lain).
Paŭlo Ebermann
4
Saya tahu ini adalah topik lama, tapi saya lebih suka kelas luar berada di paket itu sendiri dan semua antarmuka tambahan (misalnya Map.Entry) atau kelas juga berada dalam paket itu. Saya mengatakan ini karena saya ingin menjaga kelas saya singkat dan to the point. Pembaca juga dapat melihat entitas apa yang terkait dengan kelas dengan melihat kelas-kelas dalam paket. Saya mungkin punya paket java.collections.mapuntuk peta. Ini tentang OO dan modularitas. java.utilmemiliki terlalu banyak di dalamnya. utilseperti common- aroma IMO
David Kerr
1
@ Shaggy: java.utiltentu memiliki terlalu banyak di dalamnya. Yang mengatakan, saya tidak berpikir bahwa memecahnya menjadi paket berbutir halus seperti yang Anda sarankan juga ideal.
ColinD
1
@ Davidvider Seandainya Anda memanggil antarmuka ini di java.util.MapEntryluar paket sendiri. Refleks pertama: apa antarmuka yang terkait dengan kelas ini? Saya melihat ke dalam kelas. Kecuali tautan javadoc ke antarmuka ini, saya tidak tahu hubungan mereka. Plus orang tidak melihat paket impor. Setiap orang punya pendapat sendiri. Tidak ada yang benar, tidak ada yang salah
Raymond Chenon
@RaymondChenon bagian dari apa yang saya katakan adalah untuk meletakkan Map dan kelas yang terkait, misalnya Map.Entry ke dalam paket terpisah, misalnya java.collections.map. Dengan begitu semua kode yang terkait dengan peta berada dalam satu lokasi (paket) dan kelas Peta tingkat atas tidak terbebani. Saya mungkin akan meletakkan implementasi terkait (HashMap, dll) dalam paket yang sama.
David Kerr
14

Antarmuka anggota secara implisit statis. Pengubah statis dalam contoh Anda dapat dihapus tanpa mengubah semantik kode. Lihat juga Spesifikasi Bahasa Jawa 8.5.1. Deklarasi Tipe Anggota Statis

Bas Leijdekkers
sumber
Ini bukan 'antarmuka bagian dalam': ini adalah antarmuka bersarang. Batin memiliki makna khusus di Jawa.
Marquis of Lorne
@ EJP, saya membaca berbagai blog menggunakan istilah secara bergantian. Apakah antarmuka bagian dalam ada? Bagaimana mereka berbeda dari antarmuka bersarang?
Nomor 945
@BreakingBenjamin, antarmuka bersarang berarti statis. Ada kelas dalam dan kelas bersarang, tetapi tidak antarmuka bagian dalam. Bahkan jika demikian, itu harus disebut sebagai antarmuka bersarang. Batin - tidak statis dan bersarang adalah statis.
Sundar Rajan
9

Antarmuka bagian dalam harus statis agar dapat diakses. Antarmuka tidak terkait dengan instance kelas, tetapi dengan kelas itu sendiri, sehingga akan diakses dengan Foo.Bar, seperti:

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

Dalam banyak hal, ini tidak berbeda dari kelas dalam statis.

Clinton N. Dreisbach
sumber
35
Antarmuka bersarang secara otomatis statis, baik yang menulis kata kunci atau tidak.
Paŭlo Ebermann
3
Kami benar-benar membutuhkan cara bagi masyarakat untuk memilih untuk menerima jawaban yang berbeda: stackoverflow.com/a/74400/632951
Pacerier
@ ClintonN.Dreisbach Bisakah Anda menjelaskan apa yang dimaksud dengan The interface isn't associated with instances of the class, but with the class itselflebih jauh, saya tidak mengerti
Kasun Siyambalapitiya
6

Jawaban Jesse dekat, tapi saya pikir ada kode yang lebih baik untuk menunjukkan mengapa antarmuka internal mungkin berguna. Lihatlah kode di bawah ini sebelum Anda membaca. Dapatkah Anda menemukan mengapa antarmuka bagian dalam bermanfaat? Jawabannya adalah bahwa kelas DoSomethingAlready dapat dipakai dengan setiap kelas yang mengimplementasikan A dan C; bukan hanya Kebun Binatang kelas beton. Tentu saja, ini dapat dicapai bahkan jika AC tidak dalam, tetapi bayangkan menggabungkan nama yang lebih panjang (bukan hanya A dan C), dan melakukan ini untuk kombinasi lain (katakanlah, A dan B, C dan B, dll) dan Anda dengan mudah lihat bagaimana hal-hal di luar kendali. Belum lagi orang-orang yang meninjau pohon sumber Anda akan kewalahan oleh antarmuka yang hanya bermakna dalam satu kelas. Jadi, untuk meringkas,antarmuka bagian dalam memungkinkan pembangunan jenis khusus dan meningkatkan enkapsulasi mereka .

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;
  }
}
pengguna1982892
sumber
2
Ini tidak masuk akal. Kelas Zootidak tidak mengimplementasikan antarmuka AC, oleh karena itu, contoh Zookaleng tidak diteruskan ke konstruktor dari DoSomethingAlreadyyang mengharapkan AC. Fakta yang ACmeluas keduanya, Adan C, tidak menyiratkan bahwa kelas mengimplementasikan Adan Csecara ajaib juga mengimplementasikan AC.
Holger
3

Untuk menjawab pertanyaan Anda secara langsung, lihat Map.Entry.

Peta. Coba

juga ini mungkin berguna

Entri blog Static Nested Inerfaces

Henry B
sumber
4
Ini adalah contoh, tetapi bukan jawaban.
Paŭlo Ebermann
Saya menggunakan Map.Entry untuk membuat banyak objek "Pasangkan". Terkena. Implementasi Pair memiliki dua aliran pemikiran, tetapi bukan itu intinya di sini. Map.Entry mungkin bagian dalam tetapi saya menggunakannya di luar.
Ravindranath Akila
0

Biasanya saya melihat kelas dalam statis. Kelas dalam statis tidak dapat mereferensikan kelas yang berisi dimana kelas non-statis bisa. Kecuali Anda menjalankan beberapa tabrakan paket (sudah ada antarmuka yang disebut Bar dalam paket yang sama dengan Foo) Saya pikir saya akan membuatnya menjadi file sendiri. Ini juga bisa menjadi keputusan desain untuk menegakkan koneksi logis antara Foo dan Bar. Mungkin penulis bermaksud Bar hanya digunakan dengan Foo (meskipun antarmuka statis tidak akan menegakkan ini, hanya koneksi logis)

basszero
sumber
'Static inner' adalah kontradiksi dalam istilah. Kelas bersarang adalah baik statis atau batin.
Marquis of Lorne
0

Jika Anda akan mengubah kelas Foo menjadi antarmuka Foo kata kunci "publik" pada contoh di atas juga akan berlebihan karena

antarmuka yang ditentukan di dalam antarmuka lain secara implisit akan statis publik.

Danylo Volokh
sumber
Tidak menjawab pertanyaan
Hujan
0

Pada tahun 1998, Philip Wadler menyarankan perbedaan antara antarmuka statis dan antarmuka non-statis.

Sejauh yang saya bisa lihat, satu-satunya perbedaan dalam membuat antarmuka non-statis adalah sekarang bisa menyertakan kelas-kelas dalam yang tidak statis; jadi perubahan tidak akan membuat program Java yang ada tidak valid.

Misalnya, ia mengusulkan solusi untuk Masalah Ekspresi , yang merupakan ketidakcocokan antara ekspresi sebagai "seberapa banyak bahasa Anda dapat mengekspresikan" di satu sisi dan ekspresi sebagai "istilah yang Anda coba wakili dalam bahasa Anda" di sisi lain .

Contoh perbedaan antara antarmuka bersarang statis dan non-statis dapat dilihat pada kode sampelnya :

// 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);
    }
}

Sarannya tidak pernah berhasil di Java 1.5.0. Oleh karena itu, semua jawaban lain benar: tidak ada perbedaan untuk antarmuka bersarang statis dan non-statis.

Pindatjuh
sumber
Perlu dicatat bahwa semua ini mengacu pada GJ yang merupakan prosesor yang sangat awal untuk obat generik di Jawa.
Marquis of Lorne
-1

Di Java, antarmuka statis / kelas memungkinkan antarmuka / kelas untuk digunakan seperti kelas tingkat atas, yaitu, dapat dideklarasikan oleh kelas lain. Jadi, Anda bisa melakukan:

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

Tanpa statis, di atas akan gagal dikompilasi. Keuntungannya adalah Anda tidak perlu file sumber baru hanya untuk mendeklarasikan antarmuka. Itu juga secara visual mengaitkan antarmuka Bar ke kelas Foo karena Anda harus menulis Foo.Bar dan menyiratkan bahwa kelas Foo melakukan sesuatu dengan contoh Foo.Bar.

Deskripsi tipe kelas di Jawa .

Mendesis
sumber
Tanpa statis itu tidak kompilasi. 'Statis' berlebihan.
Marquis of Lorne
@ EJP: Menurut artikel yang saya tautkan, tanpa statis kelas batin hanya dapat digunakan oleh kelas pemilik dan bukan oleh apa pun di luar kelas utang. Statis menjadikannya kelas tingkat atas bersarang dan dapat digunakan oleh objek di luar ruang lingkup kelas pemilik (setidaknya, menurut artikel). Statis tidak sepenuhnya berlebihan dan mempengaruhi ruang lingkup kelas dalam. Ini berlaku untuk kelas, antarmuka selalu bertindak seperti objek tingkat atas (perlu membaca spesifikasi bahasa untuk memeriksa). Mungkin saya harus memisahkan antarmuka dan bagian kelas dari jawaban saya (saya buruk).
Skizz
-6

Static berarti bahwa setiap bagian kelas dari paket (proyek) dapat mengaksesnya tanpa menggunakan pointer. Ini dapat bermanfaat atau menghambat tergantung pada situasinya.

Contoh sempurna dari manfaat metode "statis" adalah kelas Matematika. Semua metode dalam Matematika bersifat statis. Ini berarti Anda tidak perlu keluar dari jalan Anda, membuat contoh baru, mendeklarasikan variabel dan menyimpannya dalam lebih banyak variabel, Anda bisa memasukkan data Anda dan mendapatkan hasilnya.

Statis tidak selalu berguna. Misalnya, jika Anda melakukan perbandingan kasus, Anda mungkin ingin menyimpan data dengan beberapa cara berbeda. Anda tidak dapat membuat tiga metode statis dengan tanda tangan yang identik. Anda memerlukan 3 contoh berbeda, non-statis, dan kemudian Anda bisa dan membandingkan, karena jika statis, data tidak akan berubah seiring dengan input.

Metode statis baik untuk pengembalian satu kali dan perhitungan cepat atau data yang mudah diperoleh.

Vordreller
sumber
1
Saya tahu apa artinya statis. Saya hanya belum pernah melihatnya di antarmuka sebelumnya. Karena itu pertanyaan Anda di luar topik - maaf
Mo.
1
Saya pikir jawabannya adalah di luar topik.
Koray Tugay