Loại thô là gì và tại sao chúng ta không nên sử dụng nó?


662

Câu hỏi:

  • Các kiểu thô trong Java là gì và tại sao tôi thường nghe rằng chúng không nên được sử dụng trong mã mới?
  • Lựa chọn thay thế là gì nếu chúng ta không thể sử dụng các loại nguyên liệu và làm thế nào tốt hơn?

các hướng dẫn java vẫn sử dụng JComboBox gây ra cảnh báo này. Phiên bản nào của combobox sẽ không gây ra cảnh báo này? docs.oracle.com/javase/tutorial/uiswing/components/ mài
Superstar

1
Lưu ý rằng lý do tại sao các kiểu thô tồn tại là để tương thích ngược với Java 1.4 trở lên, vốn không có chung chung.
Jesper

Câu trả lời:


744

Một loại nguyên liệu là gì?

Đặc tả ngôn ngữ Java định nghĩa một kiểu thô như sau:

JLS 4.8 Các loại thô

Một loại nguyên được định nghĩa là một trong:

  • Kiểu tham chiếu được hình thành bằng cách lấy tên của khai báo kiểu chung mà không có danh sách đối số kiểu đi kèm.

  • Một kiểu mảng có kiểu phần tử là kiểu thô.

  • Một loại không phải là staticthành viên của một loại thô Rkhông được kế thừa từ một siêu lớp hoặc siêu giao diện của R.

Đây là một ví dụ để minh họa:

public class MyType<E> {
    class Inner { }
    static class Nested { }

    public static void main(String[] args) {
        MyType mt;          // warning: MyType is a raw type
        MyType.Inner inn;   // warning: MyType.Inner is a raw type

        MyType.Nested nest; // no warning: not parameterized type
        MyType<Object> mt1; // no warning: type parameter given
        MyType<?> mt2;      // no warning: type parameter given (wildcard OK!)
    }
}

Ở đây, MyType<E>là một loại tham số ( JLS 4.5 ). Thông thường người ta thường gọi loại này là đơn giản MyType, nhưng về mặt kỹ thuật thì tên này là MyType<E>.

mt có một kiểu thô (và tạo cảnh báo biên dịch) bởi dấu đầu dòng đầu tiên trong định nghĩa trên; inncũng có một loại nguyên bởi điểm đạn thứ ba.

MyType.Nestedkhông phải là loại tham số, mặc dù đó là loại thành viên của loại được tham số hóa MyType<E>, bởi vì đó là loạistatic .

mt1mt2cả hai đều được khai báo với các tham số loại thực tế, vì vậy chúng không phải là loại thô.


Có gì đặc biệt về các loại nguyên liệu?

Về cơ bản, các kiểu thô hoạt động giống như trước khi thuốc generic được giới thiệu. Đó là, sau đây là hoàn toàn hợp pháp tại thời gian biên dịch.

List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!

Đoạn mã trên chỉ chạy tốt, nhưng giả sử bạn cũng có những điều sau đây:

for (Object o : names) {
    String name = (String) o;
    System.out.println(name);
} // throws ClassCastException!
  //    java.lang.Boolean cannot be cast to java.lang.String

Bây giờ chúng tôi gặp rắc rối vào thời gian chạy, bởi vì namescó chứa một cái gì đó không phải là một instanceof String.

Có lẽ, nếu bạn chỉ muốn nameschứa String, lẽ bạn vẫn có thể sử dụng loại thô và tự kiểm tra thủ công add , sau đó tự đúc cho Stringmọi mục từ đó names. Thậm chí tốt hơn là KHÔNG sử dụng một kiểu thô và để trình biên dịch thực hiện tất cả công việc cho bạn , khai thác sức mạnh của các tổng quát Java.

List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!

Tất nhiên, nếu bạn DO muốn namesđể cho phép một Boolean, sau đó bạn có thể khai báo nó nhưList<Object> names , và các mã trên sẽ biên dịch.

Xem thêm


Làm thế nào một loại thô khác với việc sử dụng <Object>như tham số loại?

Sau đây là trích dẫn từ Phiên bản Java hiệu quả thứ 2, Mục 23: Không sử dụng các loại thô trong mã mới :

Sự khác biệt giữa loại thô Listvà loại tham số là List<Object>gì? Nói một cách lỏng lẻo, cái trước đã từ chối kiểm tra loại chung, trong khi cái sau nói rõ ràng với trình biên dịch rằng nó có khả năng giữ các đối tượng thuộc bất kỳ loại nào. Trong khi bạn có thể truyền một List<String>tham số loại List, bạn không thể chuyển nó sang tham số loại List<Object>. Có các quy tắc phân nhóm cho generic, và List<String>là một kiểu con của kiểu thô List, nhưng không phải là kiểu tham số List<Object>. Hậu quả là bạn mất an toàn kiểu nếu bạn sử dụng kiểu thô như thế List, nhưng không mất nếu bạn sử dụng kiểu tham số nhưList<Object> .

Để minh họa điểm, hãy xem xét phương pháp sau đây lấy List<Object>và nối thêm a new Object().

void appendNewObject(List<Object> list) {
   list.add(new Object());
}

Generics trong Java là bất biến. A List<String>không phải là một List<Object>, vì vậy, sau đây sẽ tạo ra một cảnh báo trình biên dịch:

List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!

Nếu bạn đã tuyên bố appendNewObjectlấy một kiểu thô Listlàm tham số, thì điều này sẽ biên dịch và do đó bạn sẽ mất đi sự an toàn của loại mà bạn nhận được từ thuốc generic.

Xem thêm


Làm thế nào là một loại thô khác với việc sử dụng <?>như một tham số loại?

List<Object>, List<String>v.v ... là tất cả List<?>, vì vậy có thể rất hấp dẫn khi chỉ nói rằng họ chỉ là Listthay thế. Tuy nhiên, có một sự khác biệt lớn: vì chỉ một List<E>định nghĩa add(E), bạn không thể thêm bất kỳ đối tượng tùy ý nào vào a List<?>. Mặt khác, vì loại thô Listkhông có loại an toàn, bạn có thể có addbất cứ thứ gì để a List.

Hãy xem xét các biến thể sau của đoạn trước:

static void appendNewObject(List<?> list) {
    list.add(new Object()); // compilation error!
}
//...

List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!

Trình biên dịch đã làm một công việc tuyệt vời để bảo vệ bạn khỏi khả năng vi phạm kiểu bất biến của List<?>! Nếu bạn đã khai báo tham số là kiểu thô List list, thì mã sẽ biên dịch và bạn vi phạm loại bất biến List<String> names.


Một loại thô là tẩy xóa của loại đó

Quay lại JLS 4.8:

Có thể sử dụng như một kiểu xóa của một kiểu tham số hóa hoặc xóa một kiểu mảng có kiểu phần tử là kiểu tham số. Một loại như vậy được gọi là một loại thô .

[...]

Các siêu lớp (tương ứng, các siêu giao diện) của một loại thô là sự xóa của các siêu lớp (siêu giao diện) của bất kỳ tham số nào của loại chung.

Kiểu của hàm tạo, phương thức cá thể hoặc statictrường không thuộc loại thô Ckhông được kế thừa từ siêu lớp hoặc siêu giao diện của nó là kiểu thô tương ứng với kiểu xóa của loại đó trong khai báo chung tương ứng với C.

Nói một cách đơn giản hơn, khi một kiểu thô được sử dụng, các hàm tạo, phương thức cá thể và các statictrường không phải cũng bị xóa .

Lấy ví dụ sau:

class MyType<E> {
    List<String> getNames() {
        return Arrays.asList("John", "Mary");
    }

    public static void main(String[] args) {
        MyType rawType = new MyType();
        // unchecked warning!
        // required: List<String> found: List
        List<String> names = rawType.getNames();
        // compilation error!
        // incompatible types: Object cannot be converted to String
        for (String str : rawType.getNames())
            System.out.print(str);
    }
}

Khi chúng ta sử dụng phần thô MyType, getNamescũng bị xóa, để nó trả về phần thô List!

JLS 4.6 tiếp tục giải thích như sau:

Kiểu xóa cũng ánh xạ chữ ký của hàm tạo hoặc phương thức sang chữ ký không có kiểu tham số hoặc biến kiểu. Việc xóa chữ ký của hàm tạo hoặc chữ ký phương thức slà một chữ ký bao gồm cùng tên svà độ xóa của tất cả các kiểu tham số chính thức được cho trong s.

Kiểu trả về của một phương thức và các tham số loại của một phương thức chung hoặc hàm tạo cũng trải qua việc xóa nếu chữ ký của phương thức hoặc hàm tạo bị xóa.

Việc xóa chữ ký của một phương thức chung không có tham số kiểu.

Báo cáo lỗi sau đây chứa một số suy nghĩ từ Maurizio Cimadamore, một nhà biên dịch và Alex Buckley, một trong những tác giả của JLS, về lý do tại sao loại hành vi này phải xảy ra: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Nói tóm lại, nó làm cho đặc tả đơn giản hơn.)


Nếu nó không an toàn, tại sao nó được phép sử dụng loại thô?

Đây là một trích dẫn khác từ JLS 4.8:

Việc sử dụng các loại thô chỉ được phép như một sự nhượng bộ cho tính tương thích của mã kế thừa. Việc sử dụng các kiểu thô trong mã được viết sau khi đưa tính tổng quát vào ngôn ngữ lập trình Java được khuyến khích mạnh mẽ. Có thể các phiên bản tương lai của ngôn ngữ lập trình Java sẽ không cho phép sử dụng các kiểu thô.

Phiên bản Java 2 hiệu quả cũng có thêm điều này:

Cho rằng bạn không nên sử dụng các loại thô, tại sao các nhà thiết kế ngôn ngữ cho phép chúng? Để cung cấp khả năng tương thích.

Nền tảng Java sắp bước vào thập kỷ thứ hai khi các thế hệ được giới thiệu và có một lượng lớn mã Java tồn tại không sử dụng chung chung. Nó được coi là quan trọng rằng tất cả các mã này vẫn hợp pháp và có thể tương thích với mã mới sử dụng thuốc generic. Nó phải là hợp pháp để chuyển các thể hiện của các loại tham số cho các phương thức được thiết kế để sử dụng với các loại thông thường và ngược lại. Yêu cầu này, được gọi là khả năng tương thích di chuyển , đã đưa ra quyết định hỗ trợ các loại thô.

Tóm lại, các kiểu thô KHÔNG BAO GIỜ được sử dụng trong mã mới. Bạn nên luôn luôn sử dụng các loại tham số .


Không có ngoại lệ?

Thật không may, vì các tổng quát Java không được thống nhất, nên có hai trường hợp ngoại lệ trong đó các kiểu thô phải được sử dụng trong mã mới:

  • Lớp học, ví dụ List.class, khôngList<String>.class
  • instanceoftoán hạng, ví dụ o instanceof Set, khôngo instanceof Set<String>

Xem thêm


16
Ý bạn là gì, "Java generic không được thống nhất"?
Carl G

7
Đối với ngoại lệ thứ hai, cú pháp o instanceof Set<?>cũng được phép tránh kiểu thô (mặc dù nó chỉ hời hợt trong trường hợp này).
Paul Bellora

1
Các kiểu thô rất hữu ích và giảm mã soạn sẵn trong trường hợp tra cứu JNDI cho một bean mở rộng giao diện. Điều này giải quyết nhu cầu viết nđậu từ xa cho mỗi lớp thực hiện với mã giống hệt nhau.
djmj

8
"Không thống nhất" là một cách khác để nói rằng chúng bị xóa. Trình biên dịch biết các tham số chung là gì, nhưng thông tin đó không được truyền cho mã byte được tạo. JLS yêu cầu các chữ theo lớp không có tham số loại.
Erick G. Hagstrom

2
@OldCurmudgeon Thật thú vị. Ý tôi là chính thức là không , bởi vì một nghĩa đen của lớp được định nghĩa là TypeName.class, nơi TypeNamelà một định danh đơn giản ( jls ). Nói theo giả thuyết, tôi đoán nó thực sự có thể là một trong hai. Có thể là một manh mối, List<String>.classlà biến thể mà JLS đặc biệt gọi là lỗi trình biên dịch, vì vậy nếu họ thêm nó vào ngôn ngữ mà tôi mong đợi thì đó là ngôn ngữ họ sử dụng.
Radiodef

62

Các kiểu thô trong Java là gì và tại sao tôi thường nghe rằng chúng không nên được sử dụng trong mã mới?

Kiểu thô là lịch sử cổ xưa của ngôn ngữ Java. Ban đầu đã có Collectionsvà họ Objectskhông tổ chức nhiều hơn và không có gì ít hơn. Mỗi hoạt động trên Collectionsphôi yêu cầu từ Objectđến loại mong muốn.

List aList = new ArrayList();
String s = "Hello World!";
aList.add(s);
String c = (String)aList.get(0);

Trong khi điều này làm việc hầu hết thời gian, lỗi đã xảy ra

List aNumberList = new ArrayList();
String one = "1";//Number one
aNumberList.add(one);
Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here

Các bộ sưu tập không chữ cũ không thể thực thi an toàn kiểu nên lập trình viên phải nhớ những gì anh ta lưu trữ trong một bộ sưu tập.
Generics nơi được phát minh để vượt qua giới hạn này, nhà phát triển sẽ khai báo loại được lưu trữ một lần và trình biên dịch sẽ thực hiện thay thế.

List<String> aNumberList = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine

Để so sánh:

// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings

Giao diện so sánh phức tạp hơn:

//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
   int id;
   public int compareTo(Object other)
   {return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
   int id;
   public int compareTo(MyCompareAble other)
   {return this.id - other.id;}
}

Lưu ý rằng không thể thực hiện CompareAblegiao diện với compareTo(MyCompareAble)các kiểu thô. Tại sao bạn không nên sử dụng chúng:

  • Bất kỳ Objectlưu trữ trong một Collectionphải được đúc trước khi nó có thể được sử dụng
  • Sử dụng generic cho phép kiểm tra thời gian biên dịch
  • Sử dụng các kiểu thô cũng giống như lưu trữ từng giá trị như Object

Trình biên dịch làm gì: Generics tương thích ngược, chúng sử dụng các lớp java giống như các kiểu thô làm. Phép thuật xảy ra chủ yếu vào thời gian biên dịch.

List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);

Sẽ được biên dịch thành:

List someStrings = new ArrayList();
someStrings.add("one"); 
String one = (String)someStrings.get(0);

Đây là cùng một mã bạn sẽ viết nếu bạn sử dụng các loại thô trực tiếp. Tôi nghĩ rằng tôi không chắc chắn điều gì xảy ra với CompareAblegiao diện, tôi đoán rằng nó tạo ra hai compareTochức năng, một chức năngMyCompareAble và lấy Objectvà chuyển nó cho đầu tiên sau khi truyền nó.

Các lựa chọn thay thế cho các loại thô: Sử dụng thuốc generic


30

Một kiểu thô là tên của một lớp hoặc giao diện chung mà không có bất kỳ đối số kiểu nào. Ví dụ, được đưa ra lớp Box chung:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

Để tạo một loại tham số hóa Box<T>, bạn cung cấp một đối số loại thực tế cho tham số loại chính thức T:

Box<Integer> intBox = new Box<>();

Nếu đối số loại thực tế bị bỏ qua, bạn tạo một kiểu thô Box<T>:

Box rawBox = new Box();

Do đó, Boxlà loại thô của loại chung Box<T>. Tuy nhiên, một loại giao diện hoặc loại không chung chung không phải là một loại thô.

Các kiểu thô hiển thị trong mã kế thừa vì nhiều lớp API (chẳng hạn như các lớp Bộ sưu tập) không chung chung trước JDK 5.0. Khi sử dụng các loại thô, về cơ bản, bạn có được hành vi tiền tướng - một Boxcung cấp cho bạn Objects. Để tương thích ngược, việc gán loại tham số cho loại thô được cho phép:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

Nhưng nếu bạn chỉ định loại thô cho loại tham số, bạn sẽ nhận được cảnh báo:

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

Bạn cũng nhận được cảnh báo nếu bạn sử dụng loại thô để gọi các phương thức chung được xác định trong loại chung tương ứng:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

Cảnh báo cho thấy các kiểu thô bỏ qua kiểm tra kiểu chung, trì hoãn việc bắt mã không an toàn trong thời gian chạy. Do đó, bạn nên tránh sử dụng các loại thô.

Phần Loại xóa có nhiều thông tin hơn về cách trình biên dịch Java sử dụng các kiểu thô.

Thông báo lỗi không được kiểm tra

Như đã đề cập trước đây, khi trộn mã kế thừa với mã chung, bạn có thể gặp các thông báo cảnh báo tương tự như sau:

Lưu ý: example.java sử dụng các hoạt động không được kiểm tra hoặc không an toàn.

Lưu ý: Biên dịch lại với -Xlint: không được chọn để biết chi tiết.

Điều này có thể xảy ra khi sử dụng API cũ hoạt động trên các loại thô, như trong ví dụ sau:

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

Thuật ngữ "không được kiểm tra" có nghĩa là trình biên dịch không có đủ thông tin loại để thực hiện tất cả các kiểm tra loại cần thiết để đảm bảo an toàn loại. Cảnh báo "không được kiểm tra" bị tắt theo mặc định, mặc dù trình biên dịch đưa ra gợi ý. Để xem tất cả các cảnh báo "không được kiểm tra", hãy biên dịch lại với -Xlint: không được chọn.

Biên dịch lại ví dụ trước với -Xlint: không được kiểm tra cho thấy thông tin bổ sung sau:

WarningDemo.java:4: warning: [unchecked] unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning

Để vô hiệu hóa hoàn toàn các cảnh báo không được kiểm tra, hãy sử dụng cờ -Xlint: -unchecked. Các @SuppressWarnings("unchecked")chú thích ngăn chặn các cảnh báo không được kiểm soát. Nếu bạn không quen thuộc với@SuppressWarnings cú pháp, hãy xem Chú thích.

Nguồn gốc: Hướng dẫn Java


21

Một kiểu "thô" trong Java là một lớp không chung chung và liên quan đến các Đối tượng "thô", thay vì các tham số kiểu chung chung an toàn kiểu.

Ví dụ, trước khi Java generic có sẵn, bạn sẽ sử dụng một lớp bộ sưu tập như thế này:

LinkedList list = new LinkedList();
list.add(new MyObject());
MyObject myObject = (MyObject)list.get(0);

Khi bạn thêm đối tượng của mình vào danh sách, nó không quan tâm nó là loại đối tượng nào và khi bạn lấy nó từ danh sách, bạn phải chuyển nó rõ ràng sang loại bạn đang mong đợi.

Sử dụng generic, bạn loại bỏ yếu tố "không xác định", bởi vì bạn phải xác định rõ loại đối tượng nào có thể đi trong danh sách:

LinkedList<MyObject> list = new LinkedList<MyObject>();
list.add(new MyObject());
MyObject myObject = list.get(0);

Lưu ý rằng với các tổng quát, bạn không phải truyền đối tượng đến từ cuộc gọi get, bộ sưu tập được xác định trước để chỉ hoạt động với MyObject. Chính thực tế này là yếu tố lái xe chính cho thuốc generic. Nó thay đổi một nguồn lỗi thời gian chạy thành một cái gì đó có thể được kiểm tra tại thời gian biên dịch.


3
Cụ thể hơn, một loại thô là những gì bạn nhận được khi bạn chỉ cần bỏ qua các tham số loại cho một loại chung. Các loại thô thực sự chỉ là một tính năng tương thích ngược và có khả năng bị loại bỏ. Bạn có thể có hành vi tương tự bằng cách sử dụng? thông số ký tự đại diện.
John Flatness

@zerocrates: tương tự nhưng khác nhau! Sử dụng ?vẫn cung cấp loại an toàn. Tôi bao gồm nó trong câu trả lời của tôi.
đa gen

19
 private static List<String> list = new ArrayList<String>();

Bạn nên chỉ định tham số loại.

Cảnh báo khuyên rằng các loại được xác định để hỗ trợ thuốc generic nên được tham số hóa, thay vì sử dụng dạng thô của chúng.

Listđược định nghĩa để hỗ trợ thuốc generic : public class List<E>. Điều này cho phép nhiều hoạt động an toàn loại, được kiểm tra thời gian biên dịch.


3
Bây giờ được thay thế bằng suy luận kim cương trong Java 7 -private static List<String> list = new ArrayList<>();
Ian Campbell

14

Loại thô là gì và tại sao tôi thường nghe rằng chúng không nên được sử dụng trong mã mới?

"Kiểu thô" là việc sử dụng một lớp chung mà không chỉ định (các) đối số loại cho (các) loại tham số của nó, ví dụ: sử dụng Listthay vì List<String>. Khi generic được đưa vào Java, một số lớp đã được cập nhật để sử dụng generic. Sử dụng các lớp này làm "kiểu thô" (không chỉ định đối số kiểu) cho phép mã kế thừa vẫn được biên dịch.

"Loại thô" được sử dụng để tương thích ngược. Việc sử dụng chúng trong mã mới không được khuyến khích vì sử dụng lớp chung với đối số kiểu cho phép nhập mạnh hơn, do đó có thể cải thiện khả năng hiểu mã và dẫn đến việc bắt các vấn đề tiềm ẩn trước đó.

Lựa chọn thay thế là gì nếu chúng ta không thể sử dụng các loại nguyên liệu và làm thế nào tốt hơn?

Cách thay thế ưa thích là sử dụng các lớp chung như dự định - với một đối số kiểu phù hợp (ví dụ: List<String> ). Điều này cho phép lập trình viên chỉ định các loại cụ thể hơn, truyền đạt ý nghĩa hơn cho các nhà duy trì trong tương lai về mục đích sử dụng của biến hoặc cấu trúc dữ liệu và cho phép trình biên dịch thực thi an toàn loại tốt hơn. Những lợi thế này cùng nhau có thể cải thiện chất lượng mã và giúp ngăn ngừa việc đưa ra một số lỗi mã hóa.

Ví dụ: đối với phương thức mà lập trình viên muốn đảm bảo biến Danh sách có tên 'tên' chỉ chứa Chuỗi:

List<String> names = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error

1
À, rất muốn sao chép polygenelubricantscác tài liệu tham khảo "kiểu thô" từ stackoverflow.com/questions/2770111/ vào câu trả lời của riêng tôi, nhưng tôi cho rằng tôi sẽ để chúng để sử dụng trong câu trả lời của anh ấy / cô ấy.
Bert F

1
vâng, về cơ bản, tôi đã sao chép và dán phân khúc đó ở mọi nơi mọi người sử dụng các loại thô trên stackoverflow và cuối cùng quyết định chỉ có một câu hỏi để tham khảo từ bây giờ. Tôi hy vọng nó là một đóng góp tốt cho cộng đồng.
đa gen

1
@polygenelubricants Tôi nhận thấy - chúng tôi có một số câu hỏi tương tự :-)
Bert F

1
@ ha9u63ar: Thật vậy. Nói chung ngắn gọn và câu trả lời đơn giản ít nhất là tốt như câu trả lời dài và được chấp nhận.
displayName

"Syping mạnh hơn" là gì?
carloswm85

12

Trình biên dịch muốn bạn viết cái này:

private static List<String> list = new ArrayList<String>();

bởi vì nếu không, bạn có thể thêm bất kỳ loại nào bạn thích vào list, làm cho việc khởi tạo là new ArrayList<String>()vô nghĩa. Java Generics chỉ là một tính năng thời gian biên dịch, do đó, một đối tượng được tạo ra new ArrayList<String>()sẽ vui vẻ chấp nhận Integerhoặc JFramecác phần tử nếu được gán cho một tham chiếu của "kiểu thô" List- bản thân đối tượng không biết gì về loại mà nó sẽ chứa, chỉ có trình biên dịch thực hiện.


12

Ở đây tôi đang xem xét nhiều trường hợp thông qua đó bạn có thể làm rõ khái niệm

1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();

Trường hợp 1

ArrayList<String> arrnó là một ArrayListbiến tham chiếu với kiểu Stringtham chiếu đến ArralyListObject of Type String. Nó có nghĩa là nó chỉ có thể chứa Object kiểu String.

Đây là một Nghiêm để Stringkhông phải là Loại Nguyên vì vậy, Nó sẽ không bao giờ đưa ra cảnh báo.

    arr.add("hello");// alone statement will compile successfully and no warning.

    arr.add(23);  //prone to compile time error.
     //error: no suitable method found for add(int)

Trường hợp 2

Trong trường hợp ArrayList<String> arrnày là một loại nghiêm ngặt nhưng Đối tượng của bạn new ArrayList();là một loại thô.

    arr.add("hello"); //alone this compile but raise the warning.
    arr.add(23);  //again prone to compile time error.
    //error: no suitable method found for add(int)

đây arrlà một loại nghiêm ngặt. Vì vậy, nó sẽ tăng lỗi thời gian biên dịch khi thêm a integer.

Cảnh báo : - RawĐối tượng Loại được tham chiếu đến StrictLoại Biến được tham chiếu của ArrayList.

Trường hợp 3

Trong trường hợp ArrayList arrnày là loại thô nhưng Đối tượng của bạn new ArrayList<String>();là loại Nghiêm.

    arr.add("hello");  
    arr.add(23);  //compiles fine but raise the warning.

Nó sẽ thêm bất kỳ loại Đối tượng nào vào đó vì arrlà Loại Nguyên.

Cảnh báo : - StrictĐối tượng Loại được tham chiếu đến rawBiến tham chiếu loại.


8

Một nguyên -type là một thiếu một tham số kiểu khi sử dụng một kiểu generic.

Kiểu thô không nên được sử dụng vì nó có thể gây ra lỗi thời gian chạy, như chèn a doublevào cái được cho là Setcủa ints.

Set set = new HashSet();
set.add(3.45); //ok

Khi lấy các thứ từ Set, bạn không biết cái gì sắp ra. Giả sử rằng bạn mong đợi nó là tất cả int, bạn đang thực hiện nó Integer; ngoại lệ trong thời gian chạy khi double3,45 đi cùng.

Với một tham số loại được thêm vào của bạn Set, bạn sẽ nhận được một lỗi biên dịch cùng một lúc. Lỗi phòng ngừa này cho phép bạn khắc phục sự cố trước khi xảy ra sự cố trong thời gian chạy (do đó tiết kiệm thời gian và công sức).

Set<Integer> set = new HashSet<Integer>();
set.add(3.45); //NOT ok.

7

Đây là một trường hợp khác mà các loại nguyên liệu sẽ cắn bạn:

public class StrangeClass<T> {
  @SuppressWarnings("unchecked")
  public <X> X getSomethingElse() {
    return (X)"Testing something else!";
  }

  public static void main(String[] args) {
    final StrangeClass<String> withGeneric    = new StrangeClass<>();
    final StrangeClass         withoutGeneric = new StrangeClass();
    final String               value1,
                               value2;

    // Compiles
    value1 = withGeneric.getSomethingElse();

    // Produces compile error:
    // incompatible types: java.lang.Object cannot be converted to java.lang.String
    value2 = withoutGeneric.getSomethingElse();
  }
}

Như đã đề cập trong câu trả lời được chấp nhận, bạn mất tất cả sự hỗ trợ cho thuốc generic trong mã của loại thô. Mỗi tham số loại được chuyển đổi sang độ xóa của nó (trong ví dụ trên chỉ là Object).


5

Điều đang nói là của bạn listlà một Listtrong những đối tượng không xác định. Đó là Java không biết loại đối tượng nào trong danh sách. Sau đó, khi bạn muốn lặp lại danh sách, bạn phải truyền mọi phần tử, để có thể truy cập các thuộc tính của phần tử đó (trong trường hợp này là String).

Nói chung là một ý tưởng tốt hơn để tham số các bộ sưu tập, vì vậy bạn không gặp vấn đề về chuyển đổi, bạn sẽ chỉ có thể thêm các yếu tố của loại tham số và trình soạn thảo của bạn sẽ cung cấp cho bạn các phương thức phù hợp để chọn.

private static List<String> list = new ArrayList<String>();

4

trang hướng dẫn .

Một kiểu thô là tên của một lớp hoặc giao diện chung mà không có bất kỳ đối số kiểu nào. Ví dụ, được đưa ra lớp Box chung:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

Để tạo một loại Hộp được tham số hóa, bạn cung cấp một đối số loại thực tế cho tham số loại chính thức T:

Box<Integer> intBox = new Box<>();

Nếu đối số loại thực tế bị bỏ qua, bạn tạo một loại Hộp thô:

Box rawBox = new Box();

2

Tránh các loại nguyên liệu

Các kiểu thô đề cập đến việc sử dụng một kiểu chung mà không chỉ định tham số kiểu.

Ví dụ ,

Một danh sách là một loại thô, trong khi List<String>là một loại tham số.

Khi generic được giới thiệu trong JDK 1.5, các kiểu thô chỉ được giữ lại để duy trì khả năng tương thích ngược với các phiên bản Java cũ hơn. Mặc dù sử dụng các loại nguyên liệu vẫn có thể,

Họ nên tránh :

  • Họ thường yêu cầu diễn viên
  • Chúng không an toàn và một số loại lỗi quan trọng sẽ chỉ xuất hiện khi chạy
  • Chúng ít biểu cảm hơn và không tự viết tài liệu theo cách tương tự như các loại tham số hóa Ví dụ

    import java.util.*;
    
    public final class AvoidRawTypes {
    
    void withRawType() {
    
        //Raw List doesn't self-document, 
        //doesn't state explicitly what it can contain
    
        List stars = Arrays.asList("Arcturus", "Vega", "Altair");
    
        Iterator iter = stars.iterator();
    
        while (iter.hasNext()) {
    
            String star = (String) iter.next(); //cast needed
    
            log(star);
        }
    
    }
    
    void withParameterizedType() {
    
        List < String > stars = Arrays.asList("Spica", "Regulus", "Antares");
    
        for (String star: stars) {
    
            log(star);
        }
    
    }
    
    private void log(Object message) {
    
        System.out.println(Objects.toString(message));
    
    }
    
    }

Để tham khảo : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html


1

Tôi tìm thấy trang này sau khi thực hiện một số bài tập mẫu và có cùng một sự bối rối.

============== Tôi đã lấy từ mã này do mẫu cung cấp ===============

public static void main(String[] args) throws IOException {

    Map wordMap = new HashMap();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) {
        Map.Entry entry = (Map.Entry) i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

====================== Mã này ========================

public static void main(String[] args) throws IOException {
    // replace with TreeMap to get them sorted by name
    Map<String, Integer> wordMap = new HashMap<String, Integer>();
    if (args.length > 0) {
        for (int i = 0; i < args.length; i++) {
            countWord(wordMap, args[i]);
        }
    } else {
        getWordFrequency(System.in, wordMap);
    }
    for (Iterator<Entry<String, Integer>> i = wordMap.entrySet().iterator(); i.hasNext();) {
        Entry<String, Integer> entry =   i.next();
        System.out.println(entry.getKey() + " :\t" + entry.getValue());
    }

}

================================================== =============================

Nó có thể an toàn hơn nhưng mất 4 giờ để giải mã triết lý ...


0

Các loại thô là tốt khi chúng thể hiện những gì bạn muốn thể hiện.

Ví dụ, một hàm khử lưu huỳnh có thể trả về một List, nhưng nó không biết loại phần tử của danh sách. Vì vậy, Listlà loại trở lại thích hợp ở đây.


Bạn có thể dùng ? dưới dạng tham số
Dániel Kis

Có, nhưng đó là nhiều hơn để gõ và tôi chống lại gõ nhiều hơn. :)
Stefan Reich
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.