Đây là cách sử dụng thuốc generic để có được một mảng chính xác loại bạn đang tìm kiếm trong khi bảo vệ an toàn loại (trái ngược với các câu trả lời khác, sẽ cung cấp cho bạn một Object
mảng hoặc dẫn đến cảnh báo tại thời điểm biên dịch):
import java.lang.reflect.Array;
public class GenSet<E> {
private E[] a;
public GenSet(Class<E[]> clazz, int length) {
a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static void main(String[] args) {
GenSet<String> foo = new GenSet<String>(String[].class, 1);
String[] bar = foo.a;
foo.a[0] = "xyzzy";
String baz = foo.a[0];
}
}
Nó biên dịch mà không có cảnh báo, và như bạn có thể thấy main
, đối với bất kỳ loại nào bạn khai báo một thể hiện GenSet
như, bạn có thể gán a
cho một mảng của loại đó và bạn có thể gán một phần tử từ a
một biến của loại đó, nghĩa là mảng đó và các giá trị trong mảng là đúng loại.
Nó hoạt động bằng cách sử dụng các chữ theo lớp làm mã thông báo loại thời gian chạy, như được thảo luận trong Hướng dẫn Java . Lớp chữ được trình biên dịch coi là phiên bản của java.lang.Class
. Để sử dụng một, chỉ cần làm theo tên của một lớp với .class
. Vì vậy, String.class
hoạt động như một Class
đối tượng đại diện cho lớp String
. Điều này cũng hoạt động cho các giao diện, enums, mảng bất kỳ chiều (ví dụ String[].class
), nguyên thủy (ví dụ int.class
) và từ khóa void
(ví dụ void.class
).
Class
chính nó là chung (khai báo là Class<T>
, nơi T
đứng cho các loại hình mà Class
đối tượng là đại diện cho), có nghĩa là các loại String.class
là Class<String>
.
Vì vậy, bất cứ khi nào bạn gọi các nhà xây dựng cho GenSet
, bạn vượt qua trong một chữ lớp cho đối số đầu tiên đại diện cho một mảng của GenSet
kiểu được khai báo của ví dụ (ví dụ như String[].class
cho GenSet<String>
). Lưu ý rằng bạn sẽ không thể có được một loạt các nguyên hàm, vì các nguyên thủy không thể được sử dụng cho các biến loại.
Bên trong hàm tạo, việc gọi phương thức cast
trả về Object
đối số đã truyền cho lớp được đại diện bởi Class
đối tượng mà phương thức được gọi. Gọi phương thức tĩnh newInstance
trong java.lang.reflect.Array
lợi nhuận như một Object
một mảng của các loại đại diện bởi các Class
đối tượng thông qua như là đối số đầu tiên và chiều dài theo quy định của int
thông qua như là đối số thứ hai. Gọi phương thức getComponentType
trả về một Class
đối tượng đại diện cho các loại thành phần của mảng đại diện bởi các Class
đối tượng trên mà phương pháp này được gọi là (ví dụ như String.class
cho String[].class
, null
nếu Class
đối tượng không đại diện cho một mảng).
Câu cuối cùng không hoàn toàn chính xác. Gọi String[].class.getComponentType()
trả về một Class
đối tượng đại diện cho lớp String
, nhưng kiểu của nó Class<?>
thì không Class<String>
, đó là lý do tại sao bạn không thể làm một cái gì đó như sau.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
Tương tự với mọi phương thức trong Class
đó trả về một Class
đối tượng.
Về nhận xét của Joachimerer về câu trả lời này (tôi không đủ uy tín để tự nhận xét về nó), ví dụ sử dụng diễn viên T[]
sẽ dẫn đến cảnh báo vì trình biên dịch không thể đảm bảo an toàn loại trong trường hợp đó.
Chỉnh sửa liên quan đến ý kiến của Ingo:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}