2011-03-25 28 views
7

Tôi gặp lỗi trình biên dịch lạ khi sử dụng generics trong vòng lặp for-each trong Java. Đây có phải là lỗi trình biên dịch Java hay tôi thực sự thiếu một thứ gì đó ở đây?Tại sao trình biên dịch Java phàn nàn về việc sử dụng foreach với một kiểu thô?

Dưới đây là toàn bộ lớp học của tôi:

public class Generics<T extends Object> { 
    public Generics(T myObject){ 
    // I didn't really need myObject 
    } 

    public List<String> getList(){ 
    List<String> list = new ArrayList<String>(); 
    list.add("w00t StackOverflow"); 
    return list; 
    } 

    public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
} 

Trình biên dịch được phàn nàn về phù hợp với for-each: "Loại không phù hợp có thể không chuyển đổi từ loại nguyên tố đối tượng để String"
Nếu tôi thực hiện thay đổi tinh tế này, nó biên dịch:

public static void main(String...a){ 
    Generics<?> generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
    System.out.println(s); 
    } 
} 

Tôi biết getList() không Generics sử dụng, nhưng nó sử dụng chúng trong những gì tôi nghĩ là một cách hoàn toàn không liên quan. Tôi có thể hiểu điều này nếu tôi cố gắng lặp lại một thứ gì đó thuộc loại T và getList() trả về một số List<T> hoặc một cái gì đó, nhưng đó không phải là trường hợp ở đây. Kiểu trả về của getList() hoàn toàn không liên quan gì đến T và không nên quan tâm liệu tôi có sử dụng kiểu thô cho đối tượng Generics của mình hay không ... đúng không? Những thứ này không hoàn toàn không liên quan, hay tôi thực sự thiếu một thứ gì đó ở đây?

Lưu ý rằng mã cũng biên dịch nếu tôi làm điều này, mà tôi nghĩ cần phải có được tương đương với người đầu tiên cũng như:

public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    List<String> list = generics.getList(); 
    for(String s : list){ 
    System.out.println(s); 
    } 
} 
+1

'' không khác hơn ''. Bạn không tạo phiên bản chung của lớp bạn, bạn đang tạo kiểu thô. Điều này đưa chúng ta đến câu hỏi tại sao lớp học chung của bạn lại ở vị trí đầu tiên? Nơi duy nhất bạn sử dụng T là trong hàm tạo và bạn không sử dụng tham chiếu đó. – unholysampler

+0

Tôi đã sử dụng '' bởi vì tôi chỉ cần một cái gì đó cho một ví dụ. Mã thực sự rõ ràng là một cái gì đó khác, và nó sử dụng T ... nó chỉ sử dụng T theo cách hoàn toàn không liên quan đến 'getList()'. –

+0

không liên quan đến câu hỏi của bạn, nhưng tôi muốn tạo hàm tạo là Generics cls) để bạn không phải khởi tạo một đối tượng kiểu T chỉ để xây dựng lớp Generics này. – MeBigFatGuy

Trả lời

11

Sự khác biệt là khi bạn sử dụng loại thô, tất cả tham chiếu chung trong chữ ký thành viên cũng được chuyển đổi thành dạng thô của chúng. Vì vậy, hiệu quả bạn đang gọi điện thoại một phương pháp mà bây giờ có một chữ ký như thế này:

List getList() 

Bây giờ là tại sao phiên bản cuối cùng của bạn biên dịch - mặc dù nó không, có một cảnh báo nếu bạn sử dụng -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion 
    List<String> list = generics.getList(); 
             ^

này tương tự như:

List list = new ArrayList(); 
List<String> strings = list; 

... mà cũng biên dịch, nhưng với một cảnh báo dưới -Xlint.

Đạo đức của câu chuyện: không sử dụng các loại thô!

+0

Tôi rất ngạc nhiên rằng * tất cả * các tham chiếu chung trong các chữ ký thành viên được chuyển đổi thành dạng thô của chúng. Lý do để làm điều đó (bên cạnh đó Sun chỉ cảm thấy thích nó) là gì? –

+4

@Michael: JLS bao gồm thảo luận này trong phần 4.8 (loại thô): "Các kiểu thô liên quan chặt chẽ đến các ký tự đại diện. Cả hai loại đều dựa trên các loại tồn tại. Các kiểu thô có thể được coi là các ký tự đại diện có các quy tắc kiểu cố ý không có tương tác với mã cũ. " Nói cách khác, các kiểu thô thực sự không nên thường xuất hiện trong mã mới, nhưng chúng cố gắng tránh làm cho mã cũ không biên dịch được, ngay cả khi nó ít nhất là đáng ngờ. –

+0

Rất thú vị. Tôi đã biết để tránh sử dụng các loại thô (một đồng nghiệp đã viết mã để khai báo biến), nhưng điều này nhấn mạnh rằng nó thực sự có thể quan trọng. –

3

Thay đổi dòng

Generics generics = new Generics(new Object()); 

để

Generics<?> generics = new Generics<Object>(new Object()); 

Gốc của sự cố của bạn là bạn đang sử dụng raw type để loạiPhương thứclà List, không phải List<String>.

+0

Generics KHÔNG phải là chung chung của loại String ... đó là toàn bộ điểm. Chuỗi không liên quan đến loại Generics. Bất kể T 'getList()' sẽ trả về một 'List '. –

+0

@Michael McGowan, điểm tốt. Nhưng phải có một số loại liên quan đến tham số kiểu tại điểm khai báo. 'Generics generics = new Generics (...);' sẽ là tốt, modulo một cảnh báo chuyển đổi không an toàn. –

+0

@Michael McGowan, lưu ý rằng nếu tất cả những gì bạn đã làm là xóa tham số kiểu '' khỏi khai báo lớp, thì nó sẽ hoạt động. –

-1

Tôi đã thực hiện một vài điều chỉnh cho mã của bạn. Bạn thấy trong bình luận của bạn, bạn không cần Object trong constructor của bạn, do đó, cho phép loại bỏ điều đó để tránh bất kỳ sự nhầm lẫn.Thứ hai, nếu Generics sẽ là chung chung, khởi tạo nó đúng

Dưới đây là những gì các chính mới sẽ trông như

public static void main(String...a){ 
    Generics<String> generics = new Generics<String>(); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
+0

Generics KHÔNG phải là chung chung của loại String ... đó là toàn bộ điểm. Chuỗi không liên quan đến loại Generics. –

+0

Tôi nghĩ rằng bạn hiểu lầm quan điểm của tôi. Nếu bạn nhìn vào mã, bạn có phương thức getList() trả về một Danh sách . Nếu chúng ta muốn làm cho mã thực sự sạch sẽ, chúng ta có thể đã loại bỏ phần generics từ mã khác rồi trong phương thức getList(). bạn đã yêu cầu giúp đỡ để có được điều này để biên dịch chứ không phải là cách tiếp cận của mình đúng/sai. – Sean

+0

Nếu bạn rời khỏi sự giảm tốc chung ở cấp lớp, bạn mở phương thức getList() để loại bỏ mã hóa cứng tồn tại ngay bây giờ. – Sean

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