Có thể tạo lớp bên trong ẩn danh trong Java tĩnh không?


123

Trong Java, các lớp lồng nhau có thể là statichoặc không. Nếu đúng như vậy static, chúng không chứa tham chiếu đến con trỏ của thể hiện chứa (chúng cũng không được gọi là các lớp bên trong nữa, chúng được gọi là các lớp lồng nhau).

Việc quên tạo một lớp lồng nhau statickhi nó không cần tham chiếu đó có thể dẫn đến các vấn đề với việc thu gom rác hoặc phân tích thoát.

Có thể tạo ra một lớp bên trong ẩn danh statickhông? Hay trình biên dịch tự động tìm ra điều này (mà nó có thể, vì không thể có bất kỳ lớp con nào)?

Ví dụ: nếu tôi tạo một trình so sánh ẩn danh, tôi hầu như không bao giờ cần tham chiếu đến bên ngoài:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

Có vấn đề gì với "phân tích thu thập hoặc thoát rác" khi quên làm cho một lớp bên trong tĩnh không? Tôi nghĩ đây là về hiệu suất chỉ ...
Tim Buthe

17
Cá thể lớp bên trong của bạn giữ một tham chiếu đến cá thể bên ngoài của nó vẫn tồn tại, ngay cả khi bạn không cần nó. Điều này có thể giúp mọi thứ không bị thu gom rác. Hình dung một đối tượng gốc (nặng về tài nguyên) tạo ra các phiên bản nhẹ của một thứ gì đó. Sau khi nhà máy hoàn thành công việc của mình (ví dụ như trong quá trình khởi động ứng dụng), nó có thể được xử lý, nhưng điều đó chỉ hoạt động nếu những thứ nó đã tạo ra không liên kết lại.
Thilo

Tôi biết, đây chỉ là một ví dụ, nhưng vì nó là một ví dụ lặp lại, nên được đề cập rằng nó Collections.sort(list, String.CASE_INSENSITIVE_ORDER)hoạt động kể từ Java 2, hãy đọc, vì API Bộ sưu tập tồn tại…
Holger

Câu trả lời:


138

Không, bạn không thể và không, trình biên dịch không thể tìm ra nó. Đây là lý do tại sao FindBugs luôn đề xuất thay đổi các lớp ẩn danh bên trong thành các lớp staticlồng nhau được đặt tên nếu chúng không sử dụng thistham chiếu ngầm định của chúng .

Chỉnh sửa: Tom Hawtin - tackline nói rằng nếu lớp ẩn danh được tạo trong ngữ cảnh tĩnh (ví dụ như trong mainphương thức) thì trên thực tế, lớp ẩn danh là static. Nhưng JLS không đồng ý :

Một lớp ẩn danh không bao giờ abstract(§8.1.1.1). Một lớp ẩn danh luôn là một lớp bên trong (§8.1.3); nó không bao giờ static(§8.1.1, §8.5.1). Một lớp ẩn danh luôn ngầm định final(§8.1.1.2).

Bảng thuật ngữ Java của Roedy Green nói rằng thực tế là các lớp ẩn danh được phép trong ngữ cảnh tĩnh phụ thuộc vào việc triển khai:

Nếu bạn muốn ngăn cản những người duy trì mã của bạn, wags đã phát hiện ra javac.exesẽ cho phép các lớp ẩn danh bên trong staticmã và staticphương thức init , mặc dù thông số ngôn ngữ nói rằng các lớp ẩn danh là không bao giờ static. Tất nhiên, các lớp ẩn danh này không có quyền truy cập vào các trường cá thể của đối tượng. Tôi không khuyên bạn nên làm điều này. Các tính năng có thể được kéo bất cứ lúc nào.

Chỉnh sửa 2: JLS thực sự bao gồm các ngữ cảnh tĩnh rõ ràng hơn trong §15.9.2 :

Đặt C là lớp được khởi tạo, và tôi là thể hiện đang được tạo. Nếu C là một lớp bên trong thì tôi có thể có một thể hiện bao quanh ngay lập tức. Phiên bản bao quanh ngay lập tức của tôi (§8.1.3) được xác định như sau.

  • Nếu C là một lớp ẩn danh, thì:
    • Nếu biểu thức tạo phiên bản lớp xảy ra trong ngữ cảnh tĩnh (§8.1.3), thì tôi không có cá thể kèm theo ngay lập tức.
    • Nếu không, trường hợp bao quanh ngay lập tức của ithis.

Vì vậy, một lớp ẩn danh trong ngữ cảnh tĩnh gần tương đương với một staticlớp lồng nhau ở chỗ nó không giữ tham chiếu đến lớp bao quanh, mặc dù về mặt kỹ thuật nó không phải là một staticlớp.


19
+1 cho FindBugs - mọi nhà phát triển Java nên có điều này trong bản dựng của họ.
Andrew Duffy

13
Điều đó rất đáng tiếc, vì nó có nghĩa là bạn có thể muốn tránh điều này nếu không thì cú pháp gần như ngắn gọn vì lý do hiệu suất.
Thilo

2
JLS 3rd Ed giải quyết trường hợp các lớp bên trong trong ngữ cảnh tĩnh. Chúng không tĩnh theo nghĩa JLS, nhưng tĩnh theo nghĩa được đưa ra trong câu hỏi.
Tom Hawtin - tackline

6
Đây là một ví dụ về cách nó phụ thuộc vào việc triển khai: mã này in truebằng javac (sun-jdk-1.7.0_10) và falsesử dụng trình biên dịch Eclipse.
Paul Bellora

1
@MichaelMyers Tôi đã thử mô phỏng FindBugs cảnh báo tôi về việc thực hiện Nội tại Ẩn danh mà không sử dụng tham chiếu 'this' và không có gì xảy ra. Bạn có thể chứng minh cách FindBugs thông báo cho bạn như bạn đã nói ở đầu câu trả lời của mình không? Chỉ cần dán một số liên kết hoặc những gì đã từng.
Thufir Hawat

15

Đại loại. Một lớp bên trong ẩn danh được tạo trong một phương thức tĩnh rõ ràng sẽ là lớp tĩnh hiệu quả vì không có nguồn nào cho lớp bên ngoài này.

Có một số khác biệt về kỹ thuật giữa các lớp bên trong trong ngữ cảnh tĩnh và các lớp lồng nhau tĩnh. Nếu bạn quan tâm, hãy đọc JLS 3rd Ed.


Trên thực tế, tôi rút lại điều đó; JLS không đồng ý. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Một lớp ẩn danh luôn là một lớp bên trong; nó không bao giờ tĩnh."
Michael Myers

1
tĩnh theo một nghĩa khác với nghĩa trong câu hỏi.
Tom Hawtin - tackline

1
Tôi đã làm rõ thêm một chút.
Tom Hawtin - tackline

15

Tôi nghĩ rằng có một chút nhầm lẫn trong danh pháp ở đây, phải thừa nhận là quá ngớ ngẩn và khó hiểu.

Dù bạn gọi chúng là gì, những mẫu này (và một vài biến thể với khả năng hiển thị khác nhau) đều là Java bình thường, hợp pháp:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

Chúng được cung cấp trong thông số ngôn ngữ (nếu bạn thực sự bận tâm, hãy xem phần 15.9.5.1 để biết phần bên trong phương thức tĩnh).

Nhưng trích dẫn này hoàn toàn sai :

javac.exe sẽ cho phép các lớp ẩn danh bên trong mã init tĩnh và các phương thức tĩnh, mặc dù thông số ngôn ngữ nói hơn các lớp ẩn danh không bao giờ tĩnh

Tôi nghĩ rằng tác giả được trích dẫn đang nhầm lẫn giữa từ khóa tĩnh với ngữ cảnh tĩnh . (Phải thừa nhận rằng JLS cũng hơi khó hiểu về mặt này.)

Thành thật mà nói, tất cả các mẫu trên đều ổn (bạn gọi chúng là "lồng nhau", "bên trong", "ẩn danh" gì cũng được ...). Thực sự, sẽ không có ai đột ngột loại bỏ chức năng này trong phiên bản Java tiếp theo. Thành thật!


2
"(Phải thừa nhận rằng JLS cũng hơi khó hiểu về mặt này.)" Bạn đã đúng. Nghe có vẻ lạ khi nói rằng nó phụ thuộc vào việc triển khai, nhưng tôi không nhớ đã từng thấy bất kỳ lỗi rõ ràng nào trong Bảng thuật ngữ Java trước đây. Từ nay tôi coi như muối bỏ bể.
Michael Myers

2
Chúng tôi thực sự không nói về bất kỳ mô hình nào. Ý của chúng tôi là lớp lồng nhau ẩn danh là lớp tĩnh. Tức là thêm một "tĩnh" giữa newJComponenttrong ví dụ thứ ba của bạn.
Timmmm

Tôi đã thêm phần làm rõ cho câu hỏi ban đầu để hiển thị những gì được mong muốn.
Timmmm

@MichaelMyers, Chính tả trong JLS luôn cần được diễn giải.
Pacerier


0

các lớp ẩn danh bên trong không bao giờ tĩnh (chúng không thể khai báo các phương thức tĩnh hoặc trường tĩnh không cuối cùng), nhưng nếu chúng được định nghĩa trong ngữ cảnh tĩnh (phương thức tĩnh hoặc trường tĩnh) thì chúng hoạt động như tĩnh theo nghĩa là chúng không thể truy cập các thành viên không tĩnh (ví dụ) của lớp bao quanh (giống như mọi thứ khác từ ngữ cảnh tĩnh)


-3

Lưu ý về việc làm cho một lớp bên trong ẩn danh trở thành tĩnh bằng cách gọi chúng trong một phương thức tĩnh.

Điều này không thực sự xóa tham chiếu. Bạn có thể kiểm tra điều này bằng cách cố gắng tuần tự hóa lớp ẩn danh và không làm cho lớp bao quanh có thể tuần tự hóa.


5
-1: Tạo một lớp vô danh trong một phương pháp tĩnh thực hiện loại bỏ các tham chiếu đến các lớp bên ngoài. Bạn có thể kiểm tra điều này bằng cách cố gắng tuần tự hóa lớp ẩn danh và không làm cho lớp bao quanh có thể tuần tự hóa. (Tôi chỉ cần làm.)
Christian Semrau
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.