Lý do nào tôi không thể tạo các kiểu mảng chung trong Java?


273

Lý do tại sao Java không cho phép chúng tôi làm

private T[] elements = new T[initialCapacity];

Tôi có thể hiểu .NET không cho phép chúng tôi làm điều đó, vì trong .NET bạn có các loại giá trị mà tại thời điểm chạy có thể có các kích thước khác nhau, nhưng trong Java, tất cả các loại T sẽ là tham chiếu đối tượng, do đó có cùng kích thước ( sửa tôi nếu tôi sai).

Lý do là gì?


29
Bạn đang nói về cái gì vậy? Bạn hoàn toàn có thể làm điều này trong .NET. - Tôi ở đây đang cố gắng tìm hiểu tại sao tôi không thể làm điều đó trong Java.
BrainSlugs83

@ BrainSlugs83 - vui lòng thêm một liên kết đến một số ví dụ mã hoặc hướng dẫn cho thấy điều đó.
MasterJoe2


1
@ MasterJoe2 đoạn mã trên trong câu hỏi của OP là những gì tôi đang đề cập đến. Nó hoạt động tốt trong C #, nhưng không phải trong Java. - Câu hỏi cho biết nó hoạt động trong cả hai, không chính xác. - Không chắc chắn có giá trị trong việc thảo luận thêm.
BrainSlugs83

Câu trả lời:


204

Đó là bởi vì các mảng của Java (không giống như generic) chứa, trong thời gian chạy, thông tin về loại thành phần của nó. Vì vậy, bạn phải biết loại thành phần khi bạn tạo mảng. Vì bạn không biết những gì Ttrong thời gian chạy, bạn không thể tạo mảng.


29
Nhưng còn việc tẩy xóa thì sao? Tại sao không áp dụng?
Qix - MONICA ĐƯỢC PHÂN PHỐI

14
Làm thế nào để ArrayList <SomeType>làm điều đó sau đó?
Thumbz

10
@Thumbz: Ý bạn là new ArrayList<SomeType>()gì? Các loại chung không chứa tham số loại trong thời gian chạy. Tham số loại không được sử dụng trong sáng tạo. Không có sự khác biệt trong mã được tạo bởi new ArrayList<SomeType>()hoặc new ArrayList<String>()hoặc new ArrayList()cả.
newacct

8
Tôi đã hỏi thêm về cách làm ArrayList<T>việc với nó ' private T[] myArray. Ở đâu đó trong mã, nó phải có một mảng loại T chung, vậy làm thế nào?
Thumbz

21
@Thumbz: Nó không có một kiểu kiểu thời gian chạy T[]. Nó có một mảng kiểu thời gian chạy Object[]và 1) mã nguồn chứa một biến Object[](đây là cách nó nằm trong nguồn Java Java mới nhất); hoặc 2) mã nguồn chứa một biến loại T[], đó là một lời nói dối, nhưng không gây ra vấn đề do Tbị xóa bên trong phạm vi của lớp.
newacct

137

Trích dẫn:

Mảng của các loại chung không được phép vì chúng không phải là âm thanh. Vấn đề là do sự tương tác của các mảng Java, không phải là âm thanh tĩnh mà được kiểm tra động, với các tổng quát, là âm thanh tĩnh và không được kiểm tra động. Đây là cách bạn có thể khai thác lỗ hổng:

class Box<T> {
    final T x;
    Box(T x) {
        this.x = x;
    }
}

class Loophole {
    public static void main(String[] args) {
        Box<String>[] bsa = new Box<String>[3];
        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3); // error not caught by array store check
        String s = bsa[0].x; // BOOM!
    }
}

Chúng tôi đã đề xuất giải quyết vấn đề này bằng cách sử dụng các mảng an toàn tĩnh (hay còn gọi là Phương sai) đã bị từ chối cho Tiger.

- sau

(Tôi tin rằng đó là Neal Gafter , nhưng không chắc chắn)

Xem nó trong ngữ cảnh tại đây: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316


3
Lưu ý rằng tôi đã đặt nó là CW vì câu trả lời không phải của tôi.
Bart Kiers

10
Điều này giải thích tại sao nó có thể không an toàn. Nhưng các vấn đề an toàn loại có thể được cảnh báo bởi trình biên dịch. Thực tế là thậm chí không thể làm điều đó, vì gần như cùng một lý do tại sao bạn không thể làm new T(). Mỗi mảng trong Java, theo thiết kế, lưu trữ loại thành phần (nghĩa là T.class) bên trong nó; do đó bạn cần lớp T trong thời gian chạy để tạo một mảng như vậy.
newacct

2
Bạn vẫn có thể sử dụng new Box<?>[n], đôi khi có thể là đủ, mặc dù nó sẽ không giúp ích gì trong ví dụ của bạn.
Bartosz Klimek

1
@BartKiers Tôi không hiểu ... điều này vẫn không được biên dịch (java-8): tôi Box<String>[] bsa = new Box<String>[3];có thay đổi gì trong java-8 trở lên không?
Eugene

1
@Eugene, Mảng của các loại chung cụ thể đơn giản là không cho phép vì chúng có thể dẫn đến mất an toàn loại như trong mẫu. Nó không được phép trong bất kỳ phiên bản Java nào. Câu trả lời bắt đầu là "Mảng các loại chung không được phép vì chúng không có âm thanh."
garnet

47

Bằng cách không cung cấp một giải pháp tốt, bạn sẽ kết thúc với một cái gì đó tồi tệ hơn IMHO.

Các công việc phổ biến xung quanh là như sau.

T[] ts = new T[n];

được thay thế bằng (giả sử T mở rộng Object chứ không phải lớp khác)

T[] ts = (T[]) new Object[n];

Tôi thích ví dụ đầu tiên, tuy nhiên các loại học thuật hơn dường như thích thứ hai hơn, hoặc chỉ không muốn nghĩ về nó.

Hầu hết các ví dụ về lý do tại sao bạn không thể sử dụng một Đối tượng [] áp dụng như nhau cho Danh sách hoặc Bộ sưu tập (được hỗ trợ), vì vậy tôi thấy chúng là các đối số rất kém.

Lưu ý: đây là một trong những lý do thư viện Bộ sưu tập không biên dịch mà không có cảnh báo. Nếu trường hợp sử dụng này của bạn không thể được hỗ trợ mà không có cảnh báo, một cái gì đó về cơ bản đã bị phá vỡ với mô hình chung chung IMHO.


6
Bạn phải cẩn thận với cái thứ hai. Nếu bạn trả lại mảng được tạo theo cách như vậy cho ai đó mong đợi, giả sử, String[](hoặc nếu bạn lưu trữ nó trong một trường có thể truy cập công khai loại T[]và ai đó truy xuất nó), thì họ sẽ nhận được ClassCastException.
newacct

4
Tôi đã bỏ phiếu cho câu trả lời này vì ví dụ ưa thích của bạn không được phép trong Java và ví dụ thứ hai của bạn có thể ném ClassCastException
José Roberto Araújo Júnior

5
@ JoséRobertoAraújoJúnior Rõ ràng ví dụ đầu tiên cần được thay thế bằng ví dụ thứ hai. Sẽ hữu ích hơn cho bạn khi giải thích lý do tại sao ví dụ thứ hai có thể ném ClassCastException vì nó sẽ không rõ ràng với mọi người.
Peter Lawrey

3
@PeterLawrey Tôi đã tạo một câu hỏi tự trả lời cho thấy tại sao lại T[] ts = (T[]) new Object[n];là một ý tưởng tồi: stackoverflow.com/questions/21577493/ Kẻ
Jose Roberto Araújo Júnior

1
@MarkoTopolnik Tôi nên được trao huy chương vì đã trả lời tất cả các bình luận của bạn để giải thích điều tương tự tôi đã nói, điều duy nhất thay đổi so với lý do ban đầu của tôi là tôi mặc dù anh ấy nói T[] ts = new T[n];là một ví dụ hợp lệ. Tôi sẽ tiếp tục bỏ phiếu vì anh ấy trả lời có thể gây ra sự cố và nhầm lẫn cho các nhà phát triển khác và cũng không có chủ đề. Ngoài ra, tôi sẽ ngừng bình luận về điều này.
Jose Roberto Araújo Júnior

38

Mảng là covariant

Mảng được gọi là covariant, về cơ bản có nghĩa là, với các quy tắc phân nhóm của Java, một mảng kiểu T[]có thể chứa các phần tử kiểu Thoặc bất kỳ kiểu con nào T. Ví dụ

Number[] numbers = new Number[3];
numbers[0] = newInteger(10);
numbers[1] = newDouble(3.14);
numbers[2] = newByte(0);

Nhưng không chỉ có vậy, các quy tắc phân nhóm của Java cũng nói rằng một mảng S[]là một kiểu con của mảng T[]nếu Slà một kiểu con của T, do đó, một cái gì đó như thế này cũng hợp lệ:

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

Bởi vì theo các quy tắc phân nhóm trong Java, một mảng Integer[]là một kiểu con của một mảngNumber[] vì Integer là một kiểu con của Số.

Nhưng quy tắc phân nhóm này có thể dẫn đến một câu hỏi thú vị: điều gì sẽ xảy ra nếu chúng ta cố gắng làm điều này?

myNumber[0] = 3.14; //attempt of heap pollution

Dòng cuối cùng này sẽ biên dịch tốt, nhưng nếu chúng ta chạy mã này, chúng ta sẽ nhận được một ArrayStoreException vì chúng ta đang cố gắng đặt một số kép vào một mảng số nguyên. Thực tế là chúng ta đang truy cập vào mảng thông qua một tham chiếu Số không liên quan ở đây, điều quan trọng là mảng đó là một mảng các số nguyên.

Điều này có nghĩa là chúng ta có thể đánh lừa trình biên dịch, nhưng chúng ta không thể đánh lừa hệ thống loại thời gian chạy. Và điều này là như vậy bởi vì mảng là cái mà chúng ta gọi là kiểu có thể xác định lại. Điều này có nghĩa là tại thời gian chạy Java biết rằng mảng này thực sự được khởi tạo như một mảng các số nguyên mà đơn giản là được truy cập thông qua một tham chiếu kiểuNumber[] .

Vì vậy, như chúng ta có thể thấy, một điều là loại thực tế của đối tượng, một điều khác là loại tham chiếu mà chúng ta sử dụng để truy cập nó, phải không?

Vấn đề với Java Generics

Bây giờ, vấn đề với các kiểu chung trong Java là thông tin kiểu cho các tham số kiểu bị trình biên dịch loại bỏ sau khi quá trình biên dịch mã được thực hiện; do đó loại thông tin này không có sẵn trong thời gian chạy. Quá trình này được gọi là loại tẩy . Có nhiều lý do tốt để triển khai các khái quát như thế này trong Java, nhưng đó là một câu chuyện dài và nó phải làm với khả năng tương thích nhị phân với mã có sẵn.

Điểm quan trọng ở đây là vì tại thời điểm chạy không có thông tin loại, không có cách nào để đảm bảo rằng chúng tôi không phạm phải ô nhiễm đống.

Bây giờ hãy xem xét mã không an toàn sau:

List<Integer> myInts = newArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap polution

Nếu trình biên dịch Java không ngăn chúng tôi thực hiện việc này, thì hệ thống loại thời gian chạy cũng không thể ngăn chúng tôi, vì không có cách nào, vào thời gian chạy, để xác định rằng danh sách này chỉ được coi là danh sách các số nguyên. Thời gian chạy Java sẽ cho phép chúng tôi đặt bất cứ thứ gì chúng tôi muốn vào danh sách này, khi nó chỉ nên chứa các số nguyên, bởi vì khi nó được tạo, nó được khai báo là một danh sách các số nguyên. Đó là lý do tại sao trình biên dịch từ chối dòng số 4 vì nó không an toàn và nếu được phép có thể phá vỡ các giả định của hệ thống loại.

Như vậy, các nhà thiết kế của Java đã đảm bảo rằng chúng ta không thể đánh lừa trình biên dịch. Nếu chúng ta không thể đánh lừa trình biên dịch (như chúng ta có thể làm với các mảng) thì chúng ta cũng không thể đánh lừa hệ thống loại thời gian chạy.

Như vậy, chúng tôi nói rằng các loại chung là không thể xác định lại, vì tại thời điểm chạy, chúng tôi không thể xác định bản chất thực sự của loại chung.

Tôi đã bỏ qua một số phần của câu trả lời này, bạn có thể đọc toàn bộ bài viết ở đây: https://dzone.com/articles/covariance-and-contravariance


32

Lý do là không thể vì Java thực hiện Generics hoàn toàn ở cấp độ trình biên dịch và chỉ có một tệp lớp được tạo cho mỗi lớp. Điều này được gọi là Loại xóa .

Trong thời gian chạy, lớp được biên dịch cần xử lý tất cả các sử dụng của nó với cùng mã byte. Vì vậy, new T[capacity]sẽ hoàn toàn không có ý tưởng loại nào cần phải được khởi tạo.


17

Câu trả lời đã được đưa ra nhưng nếu bạn đã có Instance of T thì bạn có thể làm điều này:

T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);

Hy vọng, tôi có thể giúp, Ferdi265


1
Đây là một giải pháp tốt đẹp. Nhưng điều này sẽ nhận được các cảnh báo không được kiểm tra (chuyển từ Object sang T []). Một giải pháp "chậm hơn" nhưng "không cảnh báo" sẽ là : T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;.
midnite

1
Ngoài ra, nếu những gì chúng tôi giữ là T[] t, thì nó sẽ là (T[]) Array.newInstance(t.getClass().getComponentType(), length);. tôi đã dành một số lần để tìm ra getComponentType(). Hy vọng điều này sẽ giúp những người khác.
midnite

1
@midnite t.clone()sẽ không trở lại T[]. Bởi vì tkhông phải là Array trong câu trả lời này.
xmen

6

Lý do chính là do thực tế là các mảng trong Java là covariant.

Có một cái nhìn tổng quan tốt ở đây .


Tôi không thấy cách bạn có thể hỗ trợ "T mới [5]" ngay cả với các mảng bất biến.
Dimitris Andreou

2
@DimitrisAndreou Vâng, toàn bộ sự việc là một lỗi hài hước trong thiết kế Java. Tất cả bắt đầu với hiệp phương sai mảng. Sau đó, khi bạn có hiệp phương sai mảng, bạn có thể truyền String[]tới Objectvà lưu trữ một Integertrong đó. Vì vậy, sau đó họ phải thêm một kiểm tra loại thời gian chạy cho các cửa hàng mảng ( ArrayStoreException) vì vấn đề không thể được bắt gặp tại thời gian biên dịch. (Nếu không, một Integerthực sự có thể bị mắc kẹt trong một String[], và bạn sẽ nhận được một thông báo lỗi khi bạn cố gắng để lấy nó, đó sẽ là khủng khiếp.) ...
Radon Rosborough

2
@DimitrisAndreou hưởng Sau đó, khi bạn đã đặt kiểm tra thời gian chạy thay cho kiểm tra thời gian biên dịch âm thanh xa, bạn chạy vào loại xóa (cũng là một lỗi thiết kế không may - chỉ bao gồm để tương thích ngược). Loại xóa có nghĩa là bạn không thể thực hiện kiểm tra loại thời gian chạy cho các loại chung. Vì vậy, để tránh vấn đề loại lưu trữ mảng, bạn chỉ cần không có mảng chung. Nếu họ chỉ đơn giản là tạo ra các mảng bất biến ở vị trí đầu tiên, chúng ta chỉ có thể thực hiện kiểm tra kiểu thời gian biên dịch mà không cần chạy afoul xóa.
Radon Rosborough

Tôi vừa phát hiện ra thời gian chỉnh sửa năm phút để bình luận. Objectnên có Object[]trong bình luận đầu tiên của tôi.
Radon Rosborough

3

Tôi thích câu trả lời gián tiếp được đưa ra bởi Gafter . Tuy nhiên, tôi đề xuất nó là sai. Tôi đã thay đổi mã của Gafter một chút. Nó biên dịch và nó chạy được một lúc thì nó đánh bom nơi Gafter dự đoán nó sẽ

class Box<T> {

    final T x;

    Box(T x) {
        this.x = x;
    }
}

class Loophole {

    public static <T> T[] array(final T... values) {
        return (values);
    }

    public static void main(String[] args) {

        Box<String> a = new Box("Hello");
        Box<String> b = new Box("World");
        Box<String> c = new Box("!!!!!!!!!!!");
        Box<String>[] bsa = array(a, b, c);
        System.out.println("I created an array of generics.");

        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3);
        System.out.println("error not caught by array store check");

        try {
            String s = bsa[0].x;
        } catch (ClassCastException cause) {
            System.out.println("BOOM!");
            cause.printStackTrace();
        }
    }
}

Đầu ra là

I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Loophole.main(Box.java:26)

Vì vậy, nó xuất hiện với tôi bạn có thể tạo các kiểu mảng chung trong java. Có phải tôi đã hiểu sai câu hỏi?


Ví dụ của bạn khác với những gì tôi đã hỏi. Những gì bạn mô tả là sự nguy hiểm của hiệp phương sai mảng. Hãy xem thử (đối với .NET: blog.msdn.com/b/ericlippert/archive/2007/10/17/iêu )
elysium nuốt chửng

Hy vọng bạn nhận được một cảnh báo an toàn loại từ trình biên dịch, đúng không?
Matt McHenry

1
Có, tôi nhận được một cảnh báo an toàn loại. Vâng, tôi thấy rằng ví dụ của tôi không đáp ứng với câu hỏi.
emory

Trên thực tế, bạn nhận được nhiều cảnh báo do khởi tạo cẩu thả của a, b, c. Ngoài ra, điều này cũng được biết đến và ảnh hưởng đến thư viện lõi, ví dụ <T> java.util.Arrays.asList (T ...). Nếu bạn chuyển bất kỳ loại không thể xác nhận nào cho T, bạn sẽ nhận được cảnh báo (vì mảng được tạo có loại kém chính xác hơn mã giả vờ) và nó cực kỳ xấu. Sẽ tốt hơn nếu tác giả của phương pháp này nhận được cảnh báo, thay vì phát ra nó tại trang web sử dụng, với điều kiện là chính phương thức đó an toàn, nó không để lộ mảng cho người dùng.
Dimitris Andreou

1
Bạn đã không tạo ra một mảng chung ở đây. Trình biên dịch đã tạo ra một mảng (không chung chung) cho bạn.
newacct

2

Tôi biết tôi đến bữa tiệc muộn một chút, nhưng tôi đoán rằng tôi có thể giúp bất kỳ nhân viên nào trong tương lai vì không có câu trả lời nào trong số này trả lời vấn đề của tôi. Câu trả lời của Ferdi265 đã giúp rất nhiều.

Tôi đang cố gắng tạo danh sách Liên kết của riêng mình, vì vậy đoạn mã sau đây là thứ phù hợp với tôi:

package myList;
import java.lang.reflect.Array;

public class MyList<TYPE>  {

    private Node<TYPE> header = null;

    public void clear() {   header = null;  }

    public void add(TYPE t) {   header = new Node<TYPE>(t,header);    }

    public TYPE get(int position) {  return getNode(position).getObject();  }

    @SuppressWarnings("unchecked")
    public TYPE[] toArray() {       
        TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());        
        for(int i=0 ; i<size() ; i++)   result[i] = get(i); 
        return result;
    }


    public int size(){
         int i = 0;   
         Node<TYPE> current = header;
         while(current != null) {   
           current = current.getNext();
           i++;
        }
        return i;
    }  

Trong phương thức toArray () là cách tạo ra một mảng kiểu chung cho tôi:

TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());    

2

Trong trường hợp của tôi, tôi chỉ đơn giản muốn một loạt các ngăn xếp, đại loại như thế này:

Stack<SomeType>[] stacks = new Stack<SomeType>[2];

Vì điều này là không thể, tôi đã sử dụng như sau một cách giải quyết:

  1. Tạo một lớp bao bọc không chung chung xung quanh Stack (giả sử MyStack)
  2. MyStack [] stacks = new MyStack [2] hoạt động hoàn hảo

Xấu xí, nhưng Java hạnh phúc.

Lưu ý: như BrainSlugs83 đã đề cập trong phần bình luận cho câu hỏi, hoàn toàn có thể có các mảng tổng quát trong .NET


2

Từ hướng dẫn của Oracle :

Bạn không thể tạo các mảng của các loại tham số. Ví dụ: đoạn mã sau không biên dịch:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

Đoạn mã sau minh họa những gì xảy ra khi các loại khác nhau được chèn vào một mảng:

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

Nếu bạn thử điều tương tự với một danh sách chung, sẽ có một vấn đề:

Object[] stringLists = new List<String>[];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it.

Nếu các mảng danh sách được tham số hóa được cho phép, mã trước đó sẽ không thể ném ArrayStoreException mong muốn.

Đối với tôi, nó có vẻ rất yếu. Tôi nghĩ rằng bất kỳ ai có đủ hiểu biết về thuốc generic, sẽ hoàn toàn ổn, và thậm chí mong đợi, rằng ArrayStoredException không bị ném trong trường hợp như vậy.


0

Chắc chắn phải có một cách tốt xung quanh nó (có thể sử dụng sự phản chiếu), bởi vì dường như đó là chính xác những gì ArrayList.toArray(T[] a)nó làm. Tôi trích dẫn:

public <T> T[] toArray(T[] a)

Trả về một mảng chứa tất cả các phần tử trong danh sách này theo đúng thứ tự; kiểu thời gian chạy của mảng trả về là kiểu đã chỉ định. Nếu danh sách vừa với mảng đã chỉ định, nó sẽ được trả về trong đó. Mặt khác, một mảng mới được phân bổ với kiểu thời gian chạy của mảng được chỉ định và kích thước của danh sách này.

Vì vậy, một cách xung quanh nó sẽ là sử dụng hàm này tức là tạo một ArrayListtrong các đối tượng bạn muốn trong mảng, sau đó sử dụng toArray(T[] a)để tạo mảng thực tế. Nó sẽ không nhanh chóng, nhưng bạn đã không đề cập đến yêu cầu của bạn.

Vậy có ai biết làm thế nào toArray(T[] a)được thực hiện?


3
List.toArray (T []) hoạt động vì về cơ bản bạn đang cung cấp cho nó loại thành phần T khi chạy (bạn đang cung cấp cho nó một thể hiện của kiểu mảng mong muốn, từ đó nó có thể nhận được lớp mảng và sau đó, lớp thành phần T ). Với loại thành phần thực tế trong thời gian chạy, bạn luôn có thể tạo một mảng của loại thời gian chạy đó bằng cách sử dụng Array.newInstance(). Bạn sẽ thấy rằng được đề cập trong nhiều câu hỏi hỏi làm thế nào để tạo một mảng với một kiểu không xác định tại thời điểm biên dịch. Nhưng OP đặc biệt hỏi tại sao bạn không thể sử dụng new T[]cú pháp, đó là một câu hỏi khác
newacct

0

Đó là bởi vì generic đã được thêm vào java sau khi họ tạo ra nó, vì vậy nó hơi khó hiểu bởi vì các nhà sản xuất ban đầu của java nghĩ rằng khi tạo một mảng, kiểu sẽ được chỉ định trong quá trình tạo ra nó. Vì vậy, nó không hoạt động với generic, do đó bạn phải thực hiện E [] mảng = (E []) new Object [15]; Điều này biên dịch nhưng nó đưa ra một cảnh báo.


0

Nếu chúng ta không thể khởi tạo các mảng chung, tại sao ngôn ngữ có các kiểu mảng chung? Điểm có một loại không có đối tượng là gì?

Lý do duy nhất tôi có thể nghĩ ra, là varargs - foo(T...). Nếu không, họ có thể đã loại bỏ hoàn toàn các loại mảng chung. (Chà, họ thực sự không phải sử dụng mảng cho varargs, vì varargs không tồn tại trước 1.5 . Đó có lẽ là một sai lầm khác.)

Vì vậy, đó là một lời nói dối, bạn có thể khởi tạo các mảng chung, thông qua các varargs!

Tất nhiên, các vấn đề với mảng chung vẫn là có thật, ví dụ

static <T> T[] foo(T... args){
    return args;
}
static <T> T[] foo2(T a1, T a2){
    return foo(a1, a2);
}

public static void main(String[] args){
    String[] x2 = foo2("a", "b"); // heap pollution!
}

Chúng ta có thể sử dụng ví dụ này để thực sự chứng minh sự nguy hiểm của thuốc generic mảng .

Mặt khác, chúng tôi đã sử dụng các varargs chung trong một thập kỷ và bầu trời vẫn chưa sụp đổ. Vì vậy, chúng ta có thể lập luận rằng các vấn đề đang được phóng đại; nó không phải là vấn đề lớn. Nếu việc tạo mảng chung rõ ràng được cho phép, chúng ta sẽ có lỗi ở đây và đó; nhưng chúng ta đã quen với các vấn đề tẩy xóa và chúng ta có thể sống với nó.

Và chúng ta có thể chỉ ra foo2để bác bỏ tuyên bố rằng thông số kỹ thuật giữ chúng ta khỏi những vấn đề mà họ tuyên bố để giữ chúng ta khỏi. Nếu Sun có nhiều thời gian và tài nguyên hơn cho 1,5 , tôi tin rằng họ có thể đạt được độ phân giải thỏa mãn hơn.


0

Như những người khác đã đề cập, tất nhiên bạn có thể tạo thông qua một số thủ thuật .

Nhưng nó không được khuyến khích.

Bởi vì kiểu xóa và quan trọng hơn là covariancemảng trong đó chỉ cho phép một mảng kiểu con có thể được gán cho một mảng siêu kiểu, điều này buộc bạn phải sử dụng kiểu đúc rõ ràng khi cố gắng lấy lại giá trị gây ra thời gian chạy ClassCastExceptionlà một trong những mục tiêu chính mà generic cố gắng loại bỏ: kiểm tra loại mạnh hơn tại thời điểm biên dịch .

Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException

Một ví dụ trực tiếp hơn có thể được tìm thấy trong Java hiệu quả: Mục 25 .


hiệp phương sai : một mảng kiểu S [] là một kiểu con của T [] nếu S là một kiểu con của T


0

Nếu lớp sử dụng như một kiểu tham số hóa, nó có thể khai báo một mảng kiểu T [], nhưng nó không thể trực tiếp khởi tạo một mảng như vậy. Thay vào đó, một cách tiếp cận phổ biến là khởi tạo một mảng loại Object [], và sau đó thực hiện thu hẹp cast thành loại T [], như thể hiện trong phần sau:

  public class Portfolio<T> {
  T[] data;
 public Portfolio(int capacity) {
   data = new T[capacity];                 // illegal; compiler error
   data = (T[]) new Object[capacity];      // legal, but compiler warning
 }
 public T get(int index) { return data[index]; }
 public void set(int index, T element) { data[index] = element; }
}

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.