2016-01-26 36 views
5

trả lời nắm để Java does not support lower bounds on parameterized methods, because such a feature is "not useful enough", hãy tham khảo một similar questionTại sao trình biên dịch Java không thể suy luận đúng cách thừa kế?

Với đoạn mã sau:

package demo; 

public class Demo { 
    interface Foo { void foo(); } 
    interface Bar { void bar(); } 
    interface FooBar { 
     <R extends Foo & Bar> R foobar(); 

     static FooBar create() { return new TypicalJavaFooBar(); } 
    } 

    private static final class TypicalJavaFooBar implements Foo, Bar, FooBar { 
     public void bar() { System.out.println("foo"); } 
     public void foo() { System.out.println("bar"); } 

     public <R extends Foo & Bar> R foobar() { 
      return (R) this; 
     } 
    } 

    public static void main(String[] args) { 
     FooBar x = FooBar.create(); 
     Foo foo = x.foobar(); 
     Bar bar = x.foobar(); 
     x.foobar().foo(); 
     x.foobar().bar(); 
    } 
} 

Nếu không có các diễn viên rõ ràng để R trong TypicalJavaFooBar#foobar trình biên dịch không thành công với các lỗi sau

Error:(13, 20) java: incompatible types: demo.Demo.TypicalJavaFooBar cannot be converted to R

My câu hỏi là tại sao? Với tôi, có vẻ như trình biên dịch phải có đủ thông tin kể từ khi TypicalJavaFooBar được xác định rõ ràng để triển khai cả hai FooBar; tại sao không đủ để đáp ứng ràng buộc Foo & Bar?

CẬP NHẬT

Mục đích chính của bài tập này là để xác định các hợp đồng sau: Phương pháp foobar kêu gọi một thể hiện của một FooBar được đảm bảo để trở một cái gì đó mà thực hiện cả hai FooBar.

+0

@horatius Điều đó không liên quan gì đến câu hỏi này. – chrylis

+0

Trong 'TypicalJavaFooBar', bạn có thể thay đổi kiểu trả về của phương thức' foobar' thành 'TypicalJavaFooBar', cũng có thể bỏ đi với dàn diễn viên. Trong khi điều này không trả lời câu hỏi của bạn, đây là những gì tôi có thể sẽ làm trong tình huống này.Mặc dù tôi thường thích thành phần trên thừa kế và do đó dường như không gặp phải tình huống này. :-) – Waldheinz

+0

@Andrey, tiếc là "mục tiêu chính" của bạn không thực sự có thể có trong hệ thống kiểu Java. Bạn có thể trả lại một cái gì đó được đảm bảo để thực hiện một giao diện cụ thể, nhưng bạn không thể diễn tả "một cái gì đó thực hiện cả hai giao diện này." –

Trả lời

6

Thông số loại R được liên kết với phương pháp bằng mã gọi và về mặt lý thuyết có thể là Baz implements Foo, Bar; xem, ví dụ: Collections.emptySet(), có thông số loại được xác định bởi người gọi và có thể bị ảnh hưởng bởi một nhân chứng loại.

Để làm những gì bạn đang cố gắng thực hiện, bạn cần di chuyển thông số loại lên giao diện FooBar và có TypicalJavaFooBar implements Foo, Bar, FooBar<TypicalJavaFooBar>.

1

bạn R thể được bất kỳ loại mà là một Foo & Bar vì vậy bạn có thể viết

class MyFooBar implements Foo, Bar { ... 


FooBar x = new TypicalJavaFooBar(); 
MyFooBar mfb = x.foobar(); // this compiles now with the cast. 
0

Không, trình biên dịch không thể suy luận loại R và dàn diễn viên của mình là sai. Để xem lý do tại sao, chỉ cần mở rộng lớp học của bạn như thế này:

static class C extends TypicalJavaFooBar { 

    void eat() {} 

    @Override 
    public void bar() {} 

    @Override 
    public void foo() {} 
} 

Sau đó, dàn diễn viên của bạn cho phép này:

FooBar x = new TypicalJavaFooBar(); 
C c = x.foobar(); 
c.eat(); 

Những kết quả trong một RuntimeException:

Exception in thread "main" java.lang.ClassCastException: demo.Demo$TypicalJavaFooBar cannot be cast to demo.Demo$C 

Đó là lý do tại sao - - chắc chắn các nhà thiết kế của trình biên dịch sẽ không cho phép điều này. Có gì đó sai trong mã, do đó cần sự cần thiết của dàn diễn viên.

+0

Tôi cập nhật câu trả lời, tôi chỉ là cố gắng để giữ cho bản demo ngắn ... thành thật mà nói, tôi thấy các lớp học không phải cuối cùng, công khai hoặc lớp cuối cùng với các nhà xây dựng công cộng là một thực tế khủng khiếp kể từ khi Java 8 ra đời. Ngoài ra, bạn đang thiếu điểm. điểm ở đây là làm cho hợp đồng sau rõ ràng: phương thức gọi "foobar" trên một thể hiện của "FooBar" được đảm bảo trả về * cái gì đó * thực hiện cả "Foo" và "Bar". – Andrey

+1

Nếu bạn tìm thấy các lớp học không công khai, kinh khủng, bạn sẽ không thích các tính năng khác của ngôn ngữ, như sự cần thiết của diễn viên để cho thấy rủi ro của ClassCastException khi chạy. – smarquis

+0

phương pháp nhà máy tĩnh trong giao diện = không giao dịch với các lớp công khai. Ngoài ra, những gì * sẽ * làm cho nó hoạt động được xác định Foo & Bar như là một * thấp hơn * ràng buộc, tức là: "", tôi thấy không có lý do hợp lệ tại sao xác định giới hạn dưới không được hỗ trợ ... – Andrey

0

Nếu bạn có thể sử dụng điều này trong Java

if (obj instanceof (Foo & Bar)) 

sau đó bạn sẽ không bị buộc phải làm cho các diễn viên.

+0

sự cần thiết của diễn viên là ở đây để cho thấy rủi ro của java.lang.ClassCastException khi chạy. – smarquis

+1

Nó có thể là..IDK ... nhưng với tôi không có sự khác biệt hợp lý giữa 'if (obj instanceof (Foo & Bar))' và 'if ((obj instanceof Foo) & (obj instanceof Bar))', nhưng đầu tiên không biên dịch –

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