Lớp lồng nhau tĩnh trong Java, tại sao?


217

Tôi đã xem mã Java LinkedListvà nhận thấy rằng nó đã sử dụng một lớp lồng tĩnh , Entry.

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

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

}

Lý do cho việc sử dụng một lớp lồng tĩnh, chứ không phải là một lớp bên trong bình thường là gì?

Lý do duy nhất tôi có thể nghĩ đến, đó là Entry không có quyền truy cập vào các biến thể hiện, vì vậy theo quan điểm OOP, nó có khả năng đóng gói tốt hơn.

Nhưng tôi nghĩ có thể có những lý do khác, có thể là hiệu suất. Nó có thể là gì?

Ghi chú. Tôi hy vọng tôi đã hiểu đúng các điều khoản của mình, tôi đã gọi nó là một lớp bên trong tĩnh, nhưng tôi nghĩ điều này là sai: http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html


Câu trả lời:


271

Trang Sun mà bạn liên kết đến có một số khác biệt chính giữa hai trang:

Một lớp lồng nhau là một thành viên của lớp kèm theo của nó. Các lớp lồng nhau không tĩnh (các lớp bên trong) có quyền truy cập vào các thành viên khác của lớp kèm theo, ngay cả khi chúng được khai báo là riêng tư. Các lớp lồng nhau tĩnh không có quyền truy cập vào các thành viên khác của lớp kèm theo.
...

Lưu ý: Một lớp lồng tĩnh tương tác với các thành viên thể hiện của lớp ngoài (và các lớp khác) giống như bất kỳ lớp cấp cao nhất nào khác. Trong thực tế, một lớp lồng tĩnh là một lớp cấp cao nhất được lồng trong một lớp cấp cao nhất khác để thuận tiện cho việc đóng gói.

Không cần phải LinkedList.Entrylà lớp cấp cao nhất vì nó chỉ được sử dụng bởi LinkedList(có một số giao diện khác cũng có các lớp lồng nhau tĩnh có tên Entry, chẳng hạn như Map.Entry- cùng một khái niệm). Và vì nó không cần quyền truy cập vào các thành viên của LinkedList, nên có ý nghĩa đối với nó là tĩnh - đó là một cách tiếp cận gọn gàng hơn nhiều.

Như Jon Skeet chỉ ra , tôi nghĩ rằng một ý tưởng tốt hơn nếu bạn đang sử dụng một lớp lồng nhau là bắt đầu với nó là tĩnh, và sau đó quyết định xem nó có thực sự không tĩnh dựa trên việc sử dụng của bạn không.


Bah, tôi dường như không thể có được một liên kết neo để bình luận để làm việc, nhưng nhận xét này:#comment113712_253507
Zach Lysobey

1
@matt b Nếu một lớp lồng tĩnh không có quyền truy cập vào các thành viên thể hiện của lớp ngoài, thì nó tương tác với các thành viên thể hiện của lớp ngoài như thế nào?
Geek

1
@mattb Nhưng làm thế nào @Geek nhận thấy, trang Sun lại mâu thuẫn: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class Làm sao có thể như vậy nếu chỉ một đoạn trước các tài liệu nói rằng: Static nested classes do not have access to other members of the enclosing class Có lẽ họ muốn nói: 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 7/12/14

1
@DavidS Cảm ơn đã liên kết! Vâng, tôi đã sai, đọc bình luận của tôi bây giờ tôi thấy rằng cụm từ của tôi không chính xác. Như bạn đã nói: An inner class interacts with the instance members through an implicit reference to its enclosing classvà điều này chỉ ra một thuộc tính thú vị khác non-static inner classescũng như anonymous inner classeshoặc local classes defined inside a block: tất cả chúng đều không có hàm no-argtạo vì trình biên dịch sẽ hoàn toàn bổ sung chuỗi arg của mọi hàm tạo để truyền tham chiếu của một thể hiện của bao vây lớp học. Khá đơn giản.
tonix

1
bạn có thể sử dụng lớp bên trong tĩnh để khởi tạo lớp bên ngoài chỉ có hàm tạo riêng. Điều này được sử dụng trong mô hình xây dựng. Bạn không thể làm tương tự với lớp bên trong.
sawimurugan

47

Theo suy nghĩ của tôi, câu hỏi phải diễn ra theo cách khác bất cứ khi nào bạn nhìn thấy một lớp bên trong - nó có thực sự cần phải là một lớp bên trong không, với sự phức tạp thêm và tham chiếu IMO (chứ không phải rõ ràng và rõ ràng hơn) của lớp chứa?

Xin lưu ý bạn, tôi thiên vị với tư cách là một người hâm mộ C # - C # không tương đương với các lớp bên trong, mặc dù nó có các kiểu lồng nhau. Tôi không thể nói rằng tôi đã bỏ lỡ các lớp học bên trong :)


4
Tôi có thể sai, nhưng điều đó đối với tôi giống như một ví dụ về một lớp lồng tĩnh, không phải là một lớp bên trong. Họ thậm chí chỉ định trong ví dụ rằng họ không có quyền truy cập vào các biến thể hiện trên lớp xung quanh trong lớp lồng nhau.
ColinD

Đúng, Colin phải - C # không có các lớp bên trong, nó có các lớp lồng nhau. Xin lưu ý bạn, một lớp lồng tĩnh trong C # không giống với lớp lồng tĩnh trong Java :)
Jon Skeet

2
Các kiểu lồng nhau là một trong những lĩnh vực mà C # có nó cực kỳ chính xác so với Java. Tôi luôn ngạc nhiên về tính chính xác về ngữ nghĩa / logic của nó ..
nawfal 16/12/13

3
@nawfal: Vâng, chặn một vài câu nói đùa Tôi rất ngạc nhiên về việc ngôn ngữ C # đã được thiết kế tốt như thế nào (và được chỉ định).
Jon Skeet

1
@JonSkeet bạn có một bài viết hoặc blog về những trò đùa đó là gì không? Tôi rất thích đi qua những gì bạn tìm thấy là "niggles" :)
nawfal 16/12/13

27

Có những vấn đề duy trì bộ nhớ không rõ ràng để xem xét ở đây. Vì một lớp bên trong không tĩnh duy trì một tham chiếu ngầm cho lớp 'bên ngoài' của nó, nếu một thể hiện của lớp bên trong được tham chiếu mạnh, thì thể hiện bên ngoài cũng được tham chiếu mạnh. Điều này có thể dẫn đến một số khó khăn khi lớp bên ngoài không được thu gom rác, mặc dù có vẻ như không có gì tham chiếu đến nó.


Nếu lớp 'bên ngoài' là cuối cùng và do đó không thể được khởi tạo hoàn toàn, thì đối số này có hợp lý trong trường hợp đó không? Bởi vì nó có / giữ một tham chiếu đến một lớp bên ngoài là vô ích, nếu cái sau là cuối cùng.
gotadzeg

10

Vâng, đối với một điều, các lớp bên trong không tĩnh có một trường ẩn, ẩn chỉ đến thể hiện của lớp bên ngoài. Vì vậy, nếu lớp Entry không tĩnh, thì ngoài việc có quyền truy cập mà nó không cần, nó sẽ mang theo bốn con trỏ thay vì ba.

Theo quy tắc, tôi sẽ nói, nếu bạn định nghĩa một lớp về cơ bản là hoạt động như một tập hợp các thành viên dữ liệu, như một "struct" trong C, hãy xem xét làm cho nó tĩnh.


8

Lớp bên trong tĩnh được sử dụng trong mẫu xây dựng. Lớp bên trong tĩnh có thể khởi tạo lớp bên ngoài chỉ có hàm tạo riêng. Vì vậy, bạn có thể sử dụng lớp bên trong tĩnh để khởi tạo lớp bên ngoài chỉ có hàm tạo riêng. Bạn không thể làm tương tự với lớp bên trong vì bạn cần phải có đối tượng của lớp bên ngoài được tạo trước khi truy cập lớp bên trong.

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.
    }
}

Điều này sẽ xuất ra x: 1


7

lớp lồng nhau tĩnh giống như bất kỳ lớp bên ngoài nào khác, vì nó không có quyền truy cập vào các thành viên lớp bên ngoài.

Để thuận tiện cho việc đóng gói, chúng ta có thể ghép các lớp lồng nhau thành một lớp bên ngoài cho mục đích dễ đọc. Ngoài điều này ra, không có trường hợp sử dụng nào khác của lớp lồng tĩnh.

Ví dụ cho loại sử dụng như vậy, bạn có thể tìm thấy trong tệp Android R.java (tài nguyên). Thư mục Res của android chứa bố cục (chứa thiết kế màn hình), thư mục có thể vẽ (chứa hình ảnh được sử dụng cho dự án), thư mục giá trị (chứa hằng chuỗi), v.v.

Sine tất cả các thư mục là một phần của thư mục Res, công cụ android tạo tệp R.java (tài nguyên) chứa bên trong chứa nhiều lớp tĩnh được lồng cho mỗi thư mục bên trong của chúng.

Đây là giao diện của tệp R.java được tạo trong Android: Ở đây họ chỉ sử dụng để thuận tiện cho việc đóng gói.

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


4

Ví dụ đơn giản:

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

Nếu không tĩnh, lớp không thể được khởi tạo ngay lập tức trong một thể hiện của lớp trên (vì vậy không phải trong ví dụ trong đó main là hàm tĩnh)


Trên thực tế, StaticInnerClass của bạn không phải là một lớp tĩnh / lớp bên trong tĩnh. nó là một lớp tĩnh cấp cao nhất.
theRiley

2

Một trong những lý do cho tĩnh so với bình thường phải làm với tải lớp. Bạn không thể khởi tạo một lớp bên trong trong hàm tạo của nó.

Tái bút: Tôi luôn hiểu 'lồng nhau' và 'bên trong' có thể hoán đổi cho nhau. Có thể có các sắc thái tinh tế trong các điều khoản nhưng hầu hết các nhà phát triển Java cũng sẽ hiểu.


1

Các lớp bên trong không tĩnh có thể dẫn đến rò rỉ bộ nhớ trong khi lớp bên trong tĩnh sẽ bảo vệ chống lại chúng. Nếu lớp bên ngoài chứa dữ liệu đáng kể, nó có thể làm giảm hiệu suất của ứng dụng.


"Nội tĩnh" là một mâu thuẫn trong các điều khoản.
Hầu tước Lorne

1
@EJP, sheesh ... mọi người thực sự thoát ra bằng cách chỉ ra điều này bất cứ khi nào ai đó đề cập đến lớp bên trong tĩnh tĩnh ...
Sakiboy

0

Tôi không biết về sự khác biệt hiệu năng, nhưng như bạn nói, lớp lồng tĩnh không phải là một phần của thể hiện của lớp kèm theo. Có vẻ đơn giản hơn để tạo một lớp lồng tĩnh trừ khi bạn thực sự cần nó là một lớp bên trong.

Nó giống như lý do tại sao tôi luôn biến các biến của mình thành cuối cùng trong Java - nếu chúng không phải là cuối cùng, tôi biết có điều gì đó buồn cười xảy ra với chúng. Nếu bạn sử dụng một lớp bên trong thay vì một lớp lồng tĩnh, thì nên có một lý do chính đáng.


Một lớp innner không phải là 'một phần của thể hiện của lớp kèm theo'.
Hầu tước Lorne

một lớp bên trong tồn tại phụ thuộc vào lớp kèm theo và có quyền truy cập mật thiết đến các thành viên của lớp kèm theo, vì vậy nó thực sự là một phần của lớp kèm theo. trong thực tế, nó là một thành viên.
theRiley

0

Sử dụng một lớp lồng tĩnh thay vì không tĩnh có thể tiết kiệm không gian trong một số trường hợp. Ví dụ: thực hiện một Comparatorbên trong một lớp, nói Sinh viên.

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() {...}
  }
}

Sau đó, staticđảm bảo rằng lớp Sinh viên chỉ có một Trình so sánh, thay vì khởi tạo một lớp mới mỗi khi một thể hiện sinh viên mới được tạo.


-1

Adavantage của lớp bên trong--

  1. sử dụng một lần
  2. hỗ trợ và cải thiện việc đóng gói
  3. dễ đọc
  4. truy cập lĩnh vực tư nhân

Nếu không có lớp bên ngoài lớp bên trong sẽ không tồn tại.

class car{
    class wheel{

    }
}

Có bốn loại lớp bên trong.

  1. lớp học bình thường
  2. Phương thức lớp bên trong cục bộ
  3. Lớp bên trong vô danh
  4. lớp bên trong tĩnh

điểm ---

  1. từ lớp bên trong tĩnh, chúng ta chỉ có thể truy cập thành viên tĩnh của lớp bên ngoài.
  2. Bên trong lớp bên trong chúng tôi cananot tuyên bố thành viên tĩnh.
  3. inorder để gọi lớp bên trong bình thường trong vùng tĩnh của lớp bên ngoài.

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

  4. inorder để gọi lớp bên trong bình thường trong khu vực thể hiện của lớp bên ngoài.

    Inner i=new Inner();

  5. inorder để gọi lớp bên trong bình thường ở bên ngoài lớp bên ngoài.

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

  6. bên trong lớp bên trong Con trỏ này đến lớp bên trong.

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

  7. đối với công cụ sửa đổi lớp bên trong là - công khai, mặc định,

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

  8. bên ngoài $ bên trong là tên của tên lớp bên trong.

  9. lớp bên trong bên trong phương thức cá thể sau đó chúng ta có thể nhận được trường tĩnh và thể hiện của lớp bên ngoài.

Lớp 10.inner bên trong phương thức tĩnh thì chúng ta chỉ có thể truy cập trường tĩnh của

lớp ngoài.

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
            }
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.