2017-10-29 63 views
5
public class Base { 

    <T> List<? extends Number> f1() {return null;} 
    List<? extends Number>  f2() {return null;} 
    <T extends Number> List<T> f3() {return null; } 
} 

class Derived extends Base { 

    List<String> f1() {return null;} // compiles fine !!! 
    List<String> f3() {return null; } // compiles fine !!! 

    // compile ERR: return type is incompatible with Base.f2() 
    List<String> f2() {return null;} 
} 

Tại sao định nghĩa của phương pháp f1 trọng() và f3() trong lớp Derived cho không có lỗi biên dịch, giống như định nghĩa của trọng phương pháp f2() trong lớp Derived (mang đến cho biên dịch lỗi "kiểu trả về không tương thích với cơ sở. f2() ")?Tại sao các quy tắc đăng ký và bỏ chọn hoạt động theo cách này trên các kiểu trả về khi ghi đè một phương thức chung với một phương thức không chung chung?

Quy tắc ghi đè đăng ký trong JLS cho phép phương thức ghi đè (trong lớp bắt nguồn) không chung chung, trong khi phương thức ghi đè (trong lớp cơ sở) là chung chung.

Quy tắc ghi đè không được chọn cho phép tạo loại trả về trong phân lớp List<String> thay vì List<T> trong lớp Cơ sở. Tôi không thể giải thích sự khác biệt về hành vi bên dưới và tôi không hiểu tại sao các định nghĩa trọng số f1() và f3() trong việc biên dịch lớp gốc thành công (trên Eclipse, SE8), bỏ qua các hạn chế do tham số kiểu bị chặn cho f3() và ký tự đại diện bị ràng buộc cho f1()!

P.S. Đoán của tôi - trong f1() và f3() trong trình biên dịch có nguồn gốc xử lý cả hai phương thức như trả về chỉ "thô" List - compiler làm cho xóa (tại thời điểm này chỉ trong Derived !?) đầu tiên, và sau đó so sánh các phương pháp đã xóa trong Derived với unerased (cho đến nay) các phương thức trong Base. Bây giờ quy tắc ghi đè không được kiểm tra là OK (và không cần kiểm tra giới hạn -it chỉ đơn giản là không thể), trình biên dịch quyết định rằng việc ghi đè và biên dịch chính xác đi xa hơn ... và ở đâu đó ở cuối trình biên dịch trong Base.f1() và Base .f3() cũng bị xóa :)))

This SO answer cũng thêm ý tưởng cho chủ đề này.

Trả lời

3

f1f3 ghi đè của bạn không phải là chung chung, mặc dù các khai báo ban đầu là chung chung. Biên dịch cho phép kiểu trả về của ghi đè thay đổi từ kiểu trả về ban đầu vì chúng có cùng kiểu xóa (List). Tôi nghĩ rằng theo sau từ JLS 8.4.5, mặc dù thẳng thắn tôi thấy phần này của thông số kỹ thuật hơi khó hiểu.

Nếu bạn thay đổi ghi đè để được chung chung nữa:

<T> List<String> f1() {return null;} 
<T extends Number> List<String> f3() {return null; } 

... sau đó cả hai thất bại trong việc biên dịch:

error: <T#1>f1() in Derived cannot override <T#2>f1() in Base 
    <T> List<String> f1() {return null;} 
        ^
    return type List<String> is not compatible with List<? extends Number> 
    where T#1,T#2 are type-variables: 
    T#1 extends Object declared in method <T#1>f1() 
    T#2 extends Object declared in method <T#2>f1() 

error: <T#1>f3() in Derived cannot override <T#2>f3() in Base 
    <T extends Number> List<String> f3() {return null; } 
            ^
    return type List<String> is not compatible with List<T#1> 
    where T#1,T#2 are type-variables: 
    T#1 extends Number declared in method <T#1>f3() 
    T#2 extends Number declared in method <T#2>f3() 

Lưu ý rằng ngay cả trong mã ban đầu của bạn, javac vấn đề cảnh báo xung quanh chuyển đổi không an toàn và nếu bạn sử dụng -Xlint:unchecked, nó sẽ cung cấp chi tiết:

return type requires unchecked conversion from List<String> to List<? extends Number> 
return type requires unchecked conversion from List<String> to List<T> 
Các vấn đề liên quan