Kelas bersarang statis di Jawa, mengapa?

217

Saya sedang melihat kode Java LinkedListdan memperhatikan bahwa itu menggunakan kelas bersarang statis Entry,.

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

Apa alasan untuk menggunakan kelas bersarang statis, bukan kelas dalam normal?

Satu-satunya alasan saya bisa memikirkan, adalah bahwa Entri tidak memiliki akses ke variabel instan, jadi dari sudut pandang OOP memiliki enkapsulasi yang lebih baik.

Tapi saya pikir mungkin ada alasan lain, mungkin kinerja. Apa itu?

Catatan. Saya harap istilah saya sudah benar, saya akan menyebutnya kelas dalam statis, tetapi saya pikir ini salah: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

David Turner
sumber

Jawaban:

271

Halaman Sun yang Anda tautkan memiliki beberapa perbedaan utama antara keduanya:

Kelas bersarang adalah anggota kelas terlampir. Kelas bersarang non-statis (kelas dalam) memiliki akses ke anggota lain dari kelas terlampir, bahkan jika mereka dinyatakan pribadi. Kelas bersarang statis tidak memiliki akses ke anggota lain dari kelas terlampir.
...

Catatan: Kelas bersarang statis berinteraksi dengan anggota instance dari kelas luarnya (dan kelas lainnya) sama seperti kelas tingkat atas lainnya. Akibatnya, kelas bersarang statis pada dasarnya adalah kelas tingkat atas yang telah bersarang di kelas tingkat atas lainnya untuk kenyamanan pengemasan.

Tidak perlu LinkedList.Entrymenjadi kelas tingkat atas karena hanya digunakan oleh LinkedList(ada beberapa antarmuka lain yang juga memiliki kelas bersarang statis bernama Entry, seperti Map.Entry- konsep yang sama). Dan karena tidak memerlukan akses ke anggota LinkedList, masuk akal jika itu statis - ini adalah pendekatan yang jauh lebih bersih.

Seperti yang ditunjukkan oleh Jon Skeet , saya pikir itu adalah ide yang lebih baik jika Anda menggunakan kelas bersarang untuk memulai dengan itu menjadi statis, dan kemudian memutuskan apakah itu benar-benar perlu non-statis berdasarkan penggunaan Anda.

matt b
sumber
Bah, sepertinya saya tidak bisa mendapatkan tautan jangkar ke komentar untuk bekerja, tetapi komentar ini:#comment113712_253507
Zach Lysobey
1
@matt b Jika kelas bersarang statis tidak memiliki akses ke anggota instance dari kelas Outer, bagaimana cara berinteraksi dengan anggota instance dari kelas Outer?
Geek
1
@mattb Tapi bagaimana @ Geek telah memperhatikan, halaman Sun kontradiktif: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class Bagaimana mungkin jika hanya paragraf sebelum dokumen mengatakan bahwa: Static nested classes do not have access to other members of the enclosing class Mungkin mereka ingin mengatakan: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix
1
@ David Terima kasih atas tautannya! Ya, saya salah, membaca komentar saya sekarang saya melihat bahwa frasa saya salah. Seperti yang Anda katakan: An inner class interacts with the instance members through an implicit reference to its enclosing class, dan poin ini keluar properti lain yang menarik dari non-static inner classesserta anonymous inner classesatau local classes defined inside a block: mereka semua tidak dapat memiliki sebuah no-argkonstruktor penyebabnya compiler secara implisit akan tambahkan urutan arg setiap konstruktor untuk lulus referensi dari sebuah contoh dari melampirkan yang kelas. Cukup mudah.
tonix
1
Anda dapat menggunakan kelas dalam statis untuk membuat instance kelas luar yang hanya memiliki konstruktor pribadi. Ini digunakan dalam pola pembangun. Anda tidak dapat melakukan hal yang sama dengan kelas batin.
seenimurugan
47

Menurut saya, pertanyaannya seharusnya adalah sebaliknya ketika Anda melihat kelas dalam - apakah itu benar - benar perlu menjadi kelas dalam, dengan kompleksitas tambahan dan referensi IMO (implisit dan eksplisit) dari sebuah instance ke instance dari kelas yang mengandung?

Pikiran Anda, saya bias sebagai penggemar C # - C # tidak memiliki setara dengan kelas batin, meskipun memang memiliki tipe bersarang. Saya tidak bisa mengatakan saya telah melewatkan kelas dalam :)

Jon Skeet
sumber
4
Saya bisa saja salah, tetapi bagi saya itu seperti contoh kelas bersarang statis, bukan kelas dalam. Mereka bahkan menentukan dalam contoh bahwa mereka tidak memiliki akses ke variabel instan pada kelas sekitarnya di kelas bersarang.
ColinD
Yup, hak Colin - C # tidak memiliki kelas dalam, ia memiliki kelas bersarang. Pikiran Anda, kelas bersarang statis di C # tidak sama dengan kelas bersarang statis di Jawa :)
Jon Skeet
2
Jenis bersarang adalah salah satu daerah di mana C # mendapatkannya sangat benar dibandingkan dengan Jawa. Saya selalu kagum dengan kebenaran semantik / logisnya ..
nawfal
3
@nawfal: Ya, kecuali beberapa niggle, saya kagum pada seberapa baik bahasa C # telah dirancang (dan ditentukan)
Jon Skeet
1
@JonSkeet apakah Anda memiliki artikel atau blog tentang orang-orang niggles itu? Saya akan senang untuk pergi melalui apa yang Anda temukan sebagai "niggles" :)
nawfal
27

Ada masalah penyimpanan memori yang tidak jelas untuk diperhitungkan di sini. Karena kelas dalam non-statis mempertahankan referensi implisit untuk kelas 'luar' itu, jika turunan dari kelas batin sangat direferensikan, maka instance luar juga sangat direferensikan. Hal ini dapat menyebabkan beberapa goresan kepala ketika kelas luar tidak mengumpulkan sampah, meskipun tampaknya tidak ada yang referensi.

Leigh
sumber
Jika kelas 'luar' adalah final dan karenanya tidak bisa dipakai sama sekali, apakah argumen ini masuk akal dalam kasus itu? Karena memiliki / menjaga referensi ke kelas luar tidak berguna, jika yang terakhir adalah final.
mendapatadzeg
10

Nah, untuk satu hal, kelas dalam non-statis memiliki bidang ekstra dan tersembunyi yang menunjuk ke instance kelas luar. Jadi jika kelas Entry tidak statis, maka selain memiliki akses yang tidak perlu, itu akan membawa sekitar empat petunjuk bukan tiga.

Sebagai aturan, saya akan mengatakan, jika Anda mendefinisikan kelas yang pada dasarnya ada untuk bertindak sebagai kumpulan anggota data, seperti "struct" di C, pertimbangkan menjadikannya statis.


sumber
8

Kelas dalam statis digunakan dalam pola pembangun. Kelas dalam statis dapat instantiate kelas luarnya yang hanya memiliki konstruktor pribadi. Jadi Anda dapat menggunakan kelas dalam statis untuk membuat instance kelas luar yang hanya memiliki konstruktor pribadi. Anda tidak dapat melakukan hal yang sama dengan kelas dalam karena Anda harus memiliki objek dari kelas luar yang dibuat sebelum mengakses kelas dalam.

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

Ini akan menghasilkan x: 1

seenimurugan
sumber
7

kelas bersarang statis sama seperti kelas luar lainnya, karena tidak memiliki akses ke anggota kelas luar.

Hanya untuk kenyamanan pengemasan, kami dapat mengelompokkan kelas bersarang statis menjadi satu kelas luar untuk tujuan keterbacaan. Selain ini tidak ada kasus penggunaan lain dari kelas bersarang statis.

Contoh untuk penggunaan semacam itu, Anda dapat menemukan di file Android R.java (sumber daya). Folder res android berisi tata letak (berisi desain layar), folder yang dapat digambar (berisi gambar yang digunakan untuk proyek), folder nilai (yang berisi konstanta string), dll.

Karena semua folder adalah bagian dari folder Res, alat android menghasilkan file R.java (sumber daya) yang secara internal berisi banyak kelas bersarang statis untuk masing-masing folder dalamnya.

Berikut adalah tampilan dan nuansa file R.java yang dihasilkan di android: Di sini mereka hanya menggunakan untuk kenyamanan pengemasan.

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}
pengguna1923551
sumber
4

Contoh sederhana:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

Jika non-statis kelas tidak dapat dibuat instantiated dalam instance dari kelas atas (jadi tidak dalam contoh di mana utama adalah fungsi statis)

Vinze
sumber
StaticInnerClass Anda sebenarnya bukan kelas statis bersarang / dalam. ini adalah kelas statis tingkat atas.
theRiley
2

Salah satu alasan statis vs normal berkaitan dengan classloading. Anda tidak dapat membuat instance kelas dalam di konstruktor orang tua itu.

PS: Saya selalu mengerti 'bersarang' dan 'batin' bisa dipertukarkan. Mungkin ada nuansa halus dalam istilah ini, tetapi sebagian besar pengembang Java juga akan mengerti.

Mark Renouf
sumber
1

Kelas dalam yang tidak statis dapat menyebabkan kebocoran memori sementara kelas dalam yang statis akan melindungi mereka. Jika kelas luar menyimpan data yang cukup banyak, itu dapat menurunkan kinerja aplikasi.

Pistol
sumber
'statis batin' adalah kontradiksi dalam hal.
Marquis dari Lorne
1
@ EJP, sheesh ... orang benar-benar turun dengan menunjukkan hal ini kapan saja seseorang menyebutkan "kelas dalam statis" ...
Sakiboy
0

Saya tidak tahu tentang perbedaan kinerja, tetapi seperti yang Anda katakan, kelas bersarang statis bukan merupakan bagian dari kelas tertutup. Tampaknya lebih mudah untuk membuat kelas bersarang statis kecuali Anda benar-benar membutuhkannya untuk menjadi kelas dalam.

Agak seperti mengapa saya selalu membuat variabel saya final di Jawa - jika mereka tidak final, saya tahu ada sesuatu yang lucu terjadi dengan mereka. Jika Anda menggunakan kelas dalam bukannya kelas bersarang statis, harus ada alasan yang bagus.


sumber
Kelas innner juga bukan 'bagian dari instance dari kelas terlampir'.
Marquis of Lorne
kelas batin secara eksistensial tergantung pada kelas melampirkan, dan memiliki akses intim ke anggota kelas melampirkan, sehingga sebenarnya merupakan bagian dari kelas melampirkan. sebenarnya, itu adalah anggota.
theRiley
0

Menggunakan kelas bersarang statis daripada non-statis dapat menghemat ruang dalam beberapa kasus. Misalnya: menerapkan Comparatorkelas di dalam, katakanlah Siswa.

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

Kemudian staticmemastikan bahwa kelas Siswa hanya memiliki satu Pembanding, daripada membuat instance yang baru setiap kali instance siswa baru dibuat.

JenkinsY
sumber
-1

Keuntungan dari class-- batin

  1. sekali pakai
  2. mendukung dan meningkatkan enkapsulasi
  3. readibility
  4. akses bidang pribadi

Tanpa adanya kelas dalam maka kelas dalam tidak akan ada.

class car{
    class wheel{

    }
}

Ada empat jenis kelas batin.

  1. kelas batin yang normal
  2. Metode Kelas Batin Lokal
  3. Kelas dalam anonim
  4. kelas batin statis

titik ---

  1. dari kelas dalam statis, kita hanya dapat mengakses anggota statis kelas luar.
  2. Di dalam kelas batin kita tidak dapat mendeklarasikan anggota statis.
  3. inorder untuk memanggil kelas dalam normal di daerah statis kelas luar.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. inorder untuk memanggil kelas dalam normal misalnya area kelas luar.

    Inner i=new Inner();

  5. inorder untuk memanggil kelas dalam normal di luar kelas luar.

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. inside Inner class Ini pointer ke kelas batin.

    this.member-current inner class outerclassname.this--outer class

  7. untuk pengubah yang berlaku kelas dalam adalah - publik, default,

    final,abstract,strictfp,+private,protected,static

  8. outer $ inner adalah nama dari nama kelas dalam.

  9. kelas dalam metode instance maka kita dapat mengakses bidang statis dan instance kelas luar.

Kelas 10.inner di dalam metode statis maka kita hanya dapat mengakses bidang statis

kelas luar.

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
Gaurav Tiwari
sumber