2009-07-24 23 views
5

Tôi có một giao diệnJava Generics: Cảnh báo cần có một dàn diễn viên không được kiểm soát để phù hợp với <InterfaceName>

interface x { 
    A getValue(); 
} 

và thực hiện

class y implements x { 
    public B getValue() { return new B();} 
} 

B là một lớp con của A. này hoạt động vì hiệp biến ghi đè, tôi đoán vậy.

Nhưng nếu tôi viết lại giao diện như

interface x{ 
    <T extends A> T getValue(); 
} 

tôi nhận được một cảnh báo trong việc thực hiện mà

Cảnh báo cần có một dàn diễn viên không được kiểm soát để phù hợp với A.getValue()

Sự khác nhau giữa 2 phiên bản của giao diện là gì? Tôi đã nghĩ rằng họ là như nhau.

+0

Thật thú vị, khi tôi thay đổi nó thành "' giao diện x {T getValue();} '", cảnh báo biến mất. –

+0

Vâng, tôi nghĩ trong trường hợp này trình biên dịch có một cách để tìm ra T là gì từ khai báo lớp. Nhưng trong trường hợp khác không có cách nào để tìm ra T là gì. – Surya

Trả lời

4

Phiên bản thứ hai của giao diện là sai. Nó nói rằng getValue sẽ trả về bất kỳ lớp con nào bạn yêu cầu - kiểu trả về sẽ được suy ra dựa trên biểu thức trên tay trái của bạn.

Vì vậy, nếu bạn có được một tham chiếu đến một x (chúng ta hãy gọi nó obj), bạn một cách hợp pháp có thể thực hiện cuộc gọi sau đây mà không cảnh báo trình biên dịch:

x obj = ...; 
B b = obj.getValue(); 

Mà có lẽ không chính xác trong ví dụ của bạn, bởi vì nếu bạn thêm một lớp C mà cũng mở rộng A, bạn cũng có thể thực hiện một cách hợp pháp các cuộc gọi:

C c = obj.getValue(); 

Điều này là do T không phải là một biến kiểu thuộc giao diện, chỉ cho chính phương thức đó.

+0

Ahh ..so T được suy ra từ phía bên tay trái. Có ý nghĩa . Vì vậy, trong một ngôn ngữ mà không suy ra loại từ nhiệm vụ như C#, những gì sẽ xảy ra? – Surya

3

Về cơ bản khi bạn đang làm một <T extends A> bạn đang nói rằng bạn muốn có một lớp con cụ thể của A, không bất kỳ A, bởi vì khi đó bạn chỉ có thể làm:

A getValue(); 

Trình biên dịch được cảnh báo với bạn rằng nó có thể' t đảm bảo rằng một lớp con được xác định cụ thể của A được trả về, chỉ một chính nó.

EDIT: Để cố gắng giải thích điều này tốt hơn, B là lớp con của A, nhưng không cần phải là phân lớp duy nhất của A. Xem xét hệ thống phân cấp sau.

B kéo dài Một C kéo dài A nhưng không B

Bây giờ tôi có một phương pháp:

public void someMethod(x value) { 
    C c = x.getValue(); 
} 

Tiếp theo Generics, mà nên biên dịch và đảm bảo không có ngoại lệ thời gian chạy lớp diễn viên (có thể có khác vấn đề với giao diện ở đây nhưng chắc chắn có những trường hợp có thể được sử dụng để chứng minh điều này đúng - tôi đang giữ với ví dụ của bạn).

Nhưng điều gì sẽ xảy ra nếu bạn chuyển trường hợp y sang phương thức này?

Vâng, bây giờ tôi nhận được B thay vì C. Trình biên dịch cảnh báo bạn điều này có thể xảy ra.

+0

B là một phân lớp của A, không chắc chắn những gì bạn có nghĩa là bởi lớp con cụ thể – Surya

+1

Nếu 'lớp C' là một phân lớp của' A', sau đó tôi có thể viết 'anX. getValue() ', rõ ràng là sai đối với' y'. –

+0

(BTW: lựa chọn loại tên kém.) –

0

Vì vậy, để xây dựng trên hai câu trả lời từ AlbertoPL và Yishai, hãy thử này:

class y implements x { 
    A getValue(){ return new B();} 
} 

B kéo dài A, vì vậy đó là tốt. Người gọi không cần phải biết lớp con nào của A được trả lại, chỉ có nó mở rộng A. Vì vậy, chúng tôi đi :)

+0

Mã ban đầu đã thực sự hoạt động (với công khai). –

1

Trình biên dịch nói với bạn là không có cách nào để giải quyết chung. T không được định nghĩa ở bất kỳ đâu ngoại trừ đầu ra - mà bạn đang nói về cái gì? Nếu giao diện được generified là một loại T:

interface x <T extends A> { 
    T getValue() 
} 

thì đây sẽ làm việc, hoặc nếu phương pháp này mất một giá trị mà sẽ xác định loại trở lại bạn muốn:

interface x { 
    <T extends A> T getValue(T someArgument) 
} 

hoặc

interface x { 
    <T extends A> T getValue(Class<T> someArgument) 
} 

Sau đó, sẽ không có cảnh báo.

chỉnh sửa: Xem bài đăng của waxwing để giải thích tại sao bạn KHÔNG nên sử dụng Generics cho vấn đề cụ thể này. Tôi chỉ đơn giản cho thấy cách generics sẽ là thích hợp.

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