2013-03-11 17 views
8
class HasId<I> {} 
class HasStringId extends HasId<String> {} 
class Alert<T extends /*Some*/Object> extends HasStringId {} 
class BaseController<M extends HasId<String>> { 
    // abstract Class<M> getModelClass(); 
} 
class AlertController extends BaseController<Alert> { // error here 
    // @Override Class<Alert> getModelClass() { 
    //  return Alert.class; 
    // } 
} 

biên dịch tốt trên OpenJDK6, nhưng trong OpenJDK7 cho:Tại sao những Generics không biên dịch trong OpenJDK7, nhưng làm trong OpenJDK6

AlertController.java:50: error: type argument Alert is not within bounds of 
    type-variable T 
class AlertController extends BaseController<Alert> { 
             ^
    where T is a type-variable: 
    T extends HasId<String> declared in class BaseController 

Lưu ý rằng có cảnh báo rawtype tại dòng 50, vì Alert phải được tham số hóa. Nếu tôi làm điều đó, ví dụ: extends BaseController<Alert<Object>>, biên dịch mã. Nhưng tôi không thể làm điều đó, bởi vì tôi cần phải thực hiện getModelClass().

CẬP NHẬT: Đó là lỗi trong triển khai Java 6, được sửa trong Java 7: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182. (Và đây là câu hỏi của tôi cho các nhà phát triển trình biên dịch: http://openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1-7-0-15-but-do-in-1-6-0-27-td121820.html)

+0

Điều gì sẽ xảy ra nếu bạn tham số hóa nó là 'mở rộng BaseController > '? (Ngoài ra, thông báo lỗi phàn nàn về 'Điều khiển mở rộng ...', nhưng mã đã đăng của bạn là 'AlertController extends ...'. Bạn có chắc chắn rằng bạn có đúng dòng không?) –

+0

Tại sao có một downvote? –

+0

@TedHopp Nếu tôi tham số hóa với 'Alert ', tôi không thể triển khai 'getModelClass()'. Không thể, cho đến khi Paul Bellora đề xuất một mẹo hay để đúc Lớp đến Lớp >. @PaulBellora không phải là tôi. Cảm ơn bạn đã lừa! –

Trả lời

1

a number of trường hợp tài liệu của trình biên dịch Java 7 trở nên nghiêm ngặt hơn Java 6 trình biên dịch cho các sắc thái khác nhau. Những trường hợp này thường liên quan đến thông số ngôn ngữ thực tế đã trở nên cụ thể hơn. Lỗi này có thể liên quan đến việc sử dụng bất kỳ kiểu thô nào về cơ bản là "chọn không tham gia" các generics trên các kiểu kế thừa - cho dù đó là chính xác có thể gây tranh cãi hay không.

EDIT: Tôi không thể tìm thấy sự cố này trong số list of JDK 7 incompatibilities. Lỗi này có thể tái sản xuất using sun-jdk-1.7.0_10, nhưng không phải với trình biên dịch Eclipse (mà trong lịch sử có một hồ sơ theo dõi tốt hơn nhiều so với javac khi nói đến các sắc thái generics). Bạn nên submit an Oracle bug.

Dưới đây là một cách giải quyết có thể:

class AlertController extends BaseController<Alert<?>> { 
    @Override 
    @SuppressWarnings("unchecked") 
    Class<Alert<?>> getModelClass() { 
     return (Class<Alert<?>>)(Class<?>)Alert.class; 
    } 
} 
+1

Tôi đã tìm thấy báo cáo lỗi: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182. Cảm ơn bạn đã lừa! –

2

Câu hỏi đặt ra là liệu HasId<String> là một siêu kiểu của các loại nguyên Alert. Thông số kỹ thuật không rõ ràng về vấn đề này.

Trong tinh thần của [4.8], tất cả các loại siêu kiểu của một loại thô đều sẽ bị xóa. Vì vậy, Alert phải có siêu kiểu HasId, nhưng không phải là HasId<String>. Tuy nhiên phần chỉ nói về "siêu lớp/giao diện", không phải về "siêu kiểu".

Theo tinh thần của [4.10], siêu kiểu được phát hiện thông qua siêu kiểu trực tiếp. Không rõ phần này áp dụng cho các loại thô như thế nào. Nó có thể có ý định thống trị rằng nguyên liệu Alert có một siêu loại trực tiếp HasStringId. Điều đó có vẻ công bằng. Sau đó, vì HasId<String> là siêu mẫu trực tiếp của HasStringId, bằng cách chuyển đổi, HasId<String> là siêu kiểu của Alert!

Sự nhầm lẫn bắt nguồn từ thực tế là thực tế có hai loại HasStringId, một loại thông thường, một loại thô. Mặc dù HasStringId không phải là chung chung, nó có một kiểu siêu chung, do đó, nó có ý nghĩa để nói về phiên bản thô của HasStringId.

Thông số kỹ thuật không phân biệt giữa số HasStringId thông thường và thô. Đó là một sự giám sát.

Giả sử chúng tôi biểu thị số nguyên HasStringIdHasStringId', sau đó [4.10] có ý nghĩa hơn bây giờ. Giao diện siêu trực tiếp của raw Alert là raw HasStringId'. Giao diện siêu trực tiếp của raw HasStringId' là raw HasId. Do đó, HasId là siêu kiểu của Alert, không phải là HasId<String>.

Xem section 4 of JLS. Tôi đang liên kết với JLS trước đây ở đây, vì JLS 7 có các lỗi chỉnh sửa nghiêm trọng trong phần 4.10.2

+0

Bạn nói đúng, ít nhất những người từ [email protected] cũng nói như vậy: http://openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1 -7-0-15-nhưng-do-in-1-6-0-27-td121820.html –

1

Tôi tin rằng điều này liên quan đến cách xóa được xử lý khi không có thông số loại thực. Khi một kiểu tham số được tham chiếu mà không có bất kỳ tham số kiểu nào, tất cả các tham chiếu đến các tham số đó sẽ bị xóa.

Trong trường hợp này, bạn có một loại tham số Alert đang được sử dụng mà không có bất kỳ thông số loại nào. Thao tác này sẽ xóa tất cả các thông số loại trên Alert và các siêu lớp của nó. Điều này làm cho tham số kiểu của HasId trong mệnh đề mở rộng của HasStringId sẽ bị xóa. Alert sau đó không phân loại HasId<String>HasStringId không còn mở rộng nó mà mở rộng thêm HasId.

Cách giải quyết của Paul B. hoặc cách giải quyết khác tránh vấn đề này bằng cách luôn sử dụng Alert với thông số loại của nó.

class AlertController<T> extends BaseController<Alert<T>> { 
    @Override Class<Alert<T>> getModelClass() { 
     return cast(Alert.class); 
    } 

    @SuppressWarnings("unchecked") 
    private <T> T cast(final Object o) { 
     return (T) o; 
    } 
} 
Các vấn đề liên quan