2015-03-24 21 views
10

Lớp này:Tại sao một lớp không thể mở rộng một lớp lồng nhau tĩnh xảy ra bên trong nó?

public class OuterChild extends OuterChild.InnerParent { 
    public static class InnerParent { 
    } 
} 

Không để biên dịch:

$ javac OuterChild.java 
OuterChild.java:1: error: cyclic inheritance involving OuterChild 
public class OuterChild extends OuterChild.InnerParent { 
    ^
1 error 

OuterChild sẽ "phụ thuộc vào" bản thân, bởi vì (mỗi §8.1.4 "Superclasses and Subclasses" of The Java Language Specification, Java SE 8 Edition) một lớp trực tiếp phụ thuộc vào bất kỳ loại rằng "được đề cập trong [ của nó] extends hoặc implements khoản [& hellip;] làm vòng loại ở dạng đủ điều kiện của tên siêu lớp hoặc siêu liên kết. "

Nhưng tôi thực sự không hiểu động lực ở đây. Sự phụ thuộc có vấn đề là gì? Có phải nó chỉ phù hợp với trường hợp mà InnerParent không phải là static (và do đó sẽ kết thúc bằng một trường hợp kèm theo từ điển)?

+2

@downvoter: Hãy giải thích lý do tại sao? – ruakh

Trả lời

5

Trường hợp này có vẻ là một trường hợp góc bất chính, vì có number of bugs liên quan đến kế thừa tuần hoàn, thường dẫn đến vòng lặp vô hạn, tràn ngăn xếp và OOM trong trình biên dịch. Dưới đây là một số trích dẫn có liên quan mà có thể cung cấp một số cái nhìn sâu sắc:

Bug 4326631:

Ví dụ này là không quy phạm pháp luật, và điều này được thể hiện rõ trong thời gian tới 2nd edition của ngôn ngữ Java Specification. Các lớp đồng thời có liên quan bởi cả thừa kế và vỏ bọc đều có vấn đề, tuy nhiên, bản báo cáo lớp bên trong ban đầu không giải quyết đầy đủ vấn đề, cũng như các trình biên dịch trước 1.3 không thực hiện chính sách nhất quán. Trong ấn bản JLS 2nd , quy tắc chống lại sự thừa kế theo chu kỳ đã được mở rộng để cấm tự phân chia một lớp hoặc giao diện từ "tùy thuộc" trực tiếp hoặc gián tiếp. Loại không chỉ phụ thuộc vào các loại mà nó mở rộng hoặc triển khai, mà còn trên các loại đóng vai trò là vòng loại trong tên của các loại đó.

Bug 6695838:

Hai tờ khai lớp thực sự là cyclic; phù hợp với JLS 8.1.4 chúng tôi có mà:

Foo phụ thuộc vào Foo $ INTF (Foo $ INTF xuất hiện trong dụng cụ điều khoản của Foo)
Foo $ INTF phụ thuộc vào Moo $ INTF (Moo $ INTF xuất hiện trong mệnh đề của Foo $ INTF mở rộng)
Foo $ Intf phụ thuộc vào Foo (Foo xuất hiện như một vòng loại trong mệnh đề mở rộng của Foo $ Intf)

Vì transitivity, chúng tôi có Foo phụ thuộc vào chính nó; như vậy mã nên bị từ chối với một lỗi biên dịch thời gian.

Bug 8041994:

Đẩy mạnh trở lại, trực tiếp phụ thuộc mối quan hệ cho các lớp và các giao diện được giới thiệu vào JLS2 để làm rõ JLS1 và để trang trải superclasses/superinterfaces được lồng lớp (ví dụ AB trong Mô tả) .

Bug 6660289:

Vấn đề này là do thứ tự mà javac thực hiện ghi công của loại hình biến vọt lớp wrt ghi công.

1) Ghi công của lớp Outer < T kéo dài Outer.Inner>
1a) Ghi công của Outer gây nên ghi công của loại Outer của biến
2) Ghi công của Outer.T
2a) Ghi công của Outer.T gây nên ghi công của nó tuyên bố ràng buộc
3) Ghi công của lớp Outer.Inner < S kéo dài T>
3a) Ghi công của Outer.Inner gây nên ghi công của loại Outer.Inner của biến
4) Ghi công của Outer.Inner < S>
4a) Ghi công của Oute r.Inner.S kích hoạt thuộc tính của nó được khai báo ràng buộc
5) Attribution of Outer.T - điều này không có gì ngoài việc trả về kiểu T; như bạn có thể thấy, ở giai đoạn này T của ràng buộc chưa được đặt trên đối tượng đại diện cho loại T.

Tại thời điểm sau, đối với mỗi biến loại được gán, javac thực hiện kiểm tra để đảm bảo rằng ràng buộc của biến kiểu đã cho không đưa ra sự kế thừa tuần hoàn. Nhưng chúng ta đã thấy rằng không có ràng buộc nào được đặt cho Outer.T; cho điều này là lý do javac treo với một NPE khi cố gắng để phát hiện một chu kỳ trong cây thừa kế gây ra bởi các ràng buộc được khai báo của Outer.Inner.S.

Bug 6663588:

vọt Type-biến có thể tham khảo các lớp học thuộc về một cây thừa kế theo chu kỳ mà làm cho quá trình giải quyết để vào một vòng lặp khi nhìn lên cho các ký hiệu.

Đối với câu hỏi cụ thể của bạn "phụ thuộc vấn đề là gì?" nó dường như là một thời gian biên dịch trường hợp độ phân giải biểu tượng cạnh phức tạp, và các giải pháp được giới thiệu vào JLS2 là chỉ đơn giản là cấm chu kỳ được giới thiệu bởi các loại vòng loại cũng như siêu thực tế.

Nói cách khác, lý thuyết này có thể được thực hiện để làm việc với các cải tiến thích hợp cho trình biên dịch, nhưng cho đến khi ai đó đến và làm cho điều đó xảy ra thì thực tế hơn là chỉ cấm mối quan hệ bất thường này trong đặc tả ngôn ngữ.

+0

Cảm ơn! Bây giờ tôi đã đọc qua các lỗi mà bạn tìm thấy, và trong khi họ không nói một cách rõ ràng, "thật khó để chúng tôi từ bỏ", tôi đồng ý với đánh giá của bạn rằng đó dường như là dòng ngầm. :-P – ruakh

+0

Vâng, tôi đã tìm kiếm điều gì đó trong quy trình phát triển/tài liệu JLS2 giải thích lý do thay đổi, nhưng không tìm thấy bất kỳ điều gì rõ ràng. Tôi hình dung rằng các danh sách gửi thư thích hợp sẽ phát hiện ra một thứ gì đó như "Ugh, hãy để cấm nó và được thực hiện với nó!" :) – dimo414

2

SWAG được giáo dục: Vì JVM trước tiên phải tải lớp cha, bao gồm một lệnh để tải lớp bên trong. Lớp bên trong được xác định bởi CL sau lớp bên ngoài được định nghĩa để mọi tham chiếu đến các trường hoặc phương thức của lớp bên ngoài có thể phân giải được. Bằng cách cố gắng mở rộng bên ngoài bởi bên trong, nó yêu cầu JVM biên dịch bên trong trước bên ngoài, do đó tạo ra một vấn đề về gà và trứng. Vấn đề có nguồn gốc của nó trong thực tế là một lớp bên trong có thể tham khảo các giá trị trường lớp bên ngoài của nó cung cấp các quy tắc xung quanh phạm vi và instantiation (tĩnh v. Không tĩnh) được theo sau. Bởi vì khả năng này, JVM sẽ cần phải được đảm bảo rằng không có thời gian nào trong lớp bên trong sẽ cố gắng truy cập hoặc thay đổi bất kỳ tham chiếu trường hoặc đối tượng nào trong lớp bên ngoài. Nó chỉ có thể tìm thấy điều này bằng cách biên dịch cả hai lớp, trước hết, nhưng cần thông tin này trước để biên dịch để chắc chắn sẽ không có vấn đề về phạm vi hoặc ví dụ nào đó. Vì vậy, đó là một catch-22.

+1

Lưu ý rằng vấn đề này xảy ra tại thời gian biên dịch, không phải thời gian chạy, do đó nó không liên quan gì đến thứ tự khởi tạo lớp của JVM. Trong thực tế, nếu JVM xác định nó cần khởi tạo một lớp bên trong tĩnh nó sẽ * không * cần khởi tạo lớp ngoài trước tiên - xem [JLS §12.4.1] (https://docs.oracle.com/javase/specs/ jls/se8/html/jls-12.html # jls-12.4.1). – dimo414

Các vấn đề liên quan