it-swarm-vi.tech

Tại sao một giao diện lồng nhau tĩnh được sử dụng trong Java?

Tôi vừa tìm thấy một giao diện lồng nhau tĩnh trong cơ sở mã của chúng tôi.

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

Tôi chưa bao giờ nhìn thấy điều này trước đây. Các nhà phát triển ban đầu là ngoài tầm với. Vì vậy tôi phải hỏi SO:

Các ngữ nghĩa đằng sau một giao diện tĩnh là gì? Điều gì sẽ thay đổi, nếu tôi loại bỏ static? Tại sao mọi người sẽ làm điều này?

231
Mo.

Từ khóa tĩnh trong ví dụ trên là dự phòng (giao diện lồng nhau sẽ tự động "tĩnh") và có thể bị xóa mà không ảnh hưởng đến ngữ nghĩa; Tôi muốn giới thiệu nó được gỡ bỏ. Điều tương tự cũng xảy ra với "công khai" trên các phương thức giao diện và "chung kết chung" trên các trường giao diện - các công cụ sửa đổi là dự phòng và chỉ cần thêm lộn xộn vào mã nguồn.

Dù bằng cách nào, nhà phát triển chỉ đơn giản là khai báo một giao diện có tên Foo.Bar. Không có liên kết nào nữa với lớp kèm theo, ngoại trừ mã không thể truy cập Foo cũng sẽ không thể truy cập Foo.Bar. (Từ mã nguồn - mã byte hoặc phản chiếu có thể truy cập Foo.Bar ngay cả khi Foo là gói riêng tư!)

Kiểu này có thể chấp nhận được khi tạo giao diện lồng nhau theo cách này nếu bạn cho rằng nó chỉ được sử dụng từ lớp bên ngoài, do đó bạn không tạo tên cấp cao nhất mới. Ví dụ:

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

Câu hỏi đã được trả lời, nhưng một lý do chính đáng để sử dụng giao diện lồng nhau là nếu chức năng của nó có liên quan trực tiếp đến lớp mà nó có. Một ví dụ điển hình cho điều này là Listener. Nếu bạn có một lớp Foo và bạn muốn các lớp khác có thể nghe các sự kiện trên đó, bạn có thể khai báo một giao diện có tên FooListener, điều đó có thể rõ ràng hơn, nhưng có lẽ sẽ rõ ràng hơn khi khai báo một giao diện lồng nhau và có các lớp khác thực hiện Foo.Listener (một lớp lồng nhau Foo.Event không tệ với điều này).

71
ColinD

Giao diện thành viên là hoàn toàn tĩnh. Công cụ sửa đổi tĩnh trong ví dụ của bạn có thể được gỡ bỏ mà không thay đổi ngữ nghĩa của mã. Xem thêm Java Đặc tả ngôn ngữ 8.5.1. Tuyên bố loại thành viên tĩnh

14
Bas Leijdekkers

Một giao diện bên trong phải tĩnh để được truy cập. Giao diện không được liên kết với các thể hiện của lớp, nhưng với chính lớp đó, vì vậy nó sẽ được truy cập bằng Foo.Bar, như vậy:

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

Trong hầu hết các cách, điều này không khác với một lớp bên trong tĩnh.

9
Clinton N. Dreisbach

Câu trả lời của Jesse rất gần, nhưng tôi nghĩ rằng có một mã tốt hơn để chứng minh tại sao một giao diện bên trong có thể hữu ích. Nhìn vào mã dưới đây trước khi bạn đọc tiếp. Bạn có thể tìm thấy tại sao giao diện bên trong là hữu ích? Câu trả lời là lớp DoS SomethingAl yet có thể được khởi tạo với bất kì lớp thực hiện A và C; Không chỉ là sở thú lớp học cụ thể. Tất nhiên, điều này có thể đạt được ngay cả khi AC không nằm trong, nhưng hãy tưởng tượng ghép các tên dài hơn (không chỉ A và C) và thực hiện điều này cho các kết hợp khác (giả sử A và B, C và B, v.v.) và bạn dễ dàng xem mọi thứ vượt quá tầm kiểm soát Chưa kể rằng mọi người đánh giá cây nguồn của bạn sẽ bị choáng ngợp bởi các giao diện chỉ có ý nghĩa trong một lớp. Vì vậy, để tóm tắt, một giao diện bên trong cho phép xây dựng các loại tùy chỉnh và cải thiện việc đóng gói của chúng.

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;
  }
}
6
user1982892

Để trả lời câu hỏi của bạn rất trực tiếp, hãy nhìn vào Map.Entry.

Bản đồ.Entry

điều này cũng có thể hữu ích

Mục nhập blog inedfaces Nested Inedfaces

3
Henry B

Năm 1998, Philip Wadler đã đề xuất một sự khác biệt giữa giao diện tĩnh và giao diện không tĩnh.

Theo như tôi có thể thấy, sự khác biệt duy nhất trong việc làm cho một giao diện không tĩnh là bây giờ nó có thể bao gồm các lớp bên trong không tĩnh; do đó, thay đổi sẽ không hiển thị không hợp lệ bất kỳ chương trình Java nào hiện có.

Ví dụ: anh ấy đã đề xuất một giải pháp cho Vấn đề biểu hiện , đó là sự không phù hợp giữa biểu thức là "ngôn ngữ của bạn có thể diễn tả được bao nhiêu" trên một mặt và biểu hiện là "thuật ngữ bạn đang cố gắng thể hiện trong ngôn ngữ của bạn "mặt khác.

Có thể thấy một ví dụ về sự khác biệt giữa các giao diện lồng nhau tĩnh và không tĩnh trong mã mẫu của anh ấy :

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

Gợi ý của anh ấy không bao giờ được thực hiện trong Java 1.5.0. Do đó, tất cả các câu trả lời khác đều đúng: không có sự khác biệt nào đối với các giao diện lồng nhau tĩnh và không tĩnh.

0
Pindatjuh

Nếu bạn sẽ thay đổi lớp Foo thành giao diện Foo, từ khóa "công khai" trong ví dụ trên cũng sẽ bị dư thừa vì

giao diện được xác định bên trong giao diện khác sẽ hoàn toàn công khai tĩnh.

0
Danylo Volokh

Thông thường tôi thấy các lớp bên trong tĩnh. Các lớp bên trong tĩnh không thể tham chiếu các lớp chứa trong đó các lớp không tĩnh có thể. Trừ khi bạn gặp phải một số xung đột gói (đã có giao diện có tên là Bar trong cùng gói với Foo) Tôi nghĩ tôi sẽ biến nó thành tệp riêng. Nó cũng có thể là một quyết định thiết kế để thực thi kết nối hợp lý giữa Foo và Bar. Có lẽ tác giả dự định Bar chỉ được sử dụng với Foo (mặc dù giao diện bên trong tĩnh sẽ không thực thi điều này, chỉ là một kết nối hợp lý)

0
basszero