2012-12-08 33 views

Trả lời

7

Trong Java, việc tạo một mảng kiểu chung không đơn giản.

Phương pháp đơn giản không biên dịch:

public class Container<E> { 

    E[] arr = new E[3]; // ERROR: Cannot create a generic array of E 

} 

Thay E với Object, và tất cả là tốt (tại các chi phí của thêm phức tạp ở những nơi khác trong việc thực hiện container).

Có các cách tiếp cận khác, nhưng chúng trình bày một loạt các sự cân bằng khác nhau. Đối với một cuộc thảo luận sâu rộng, xem How to create a generic array in Java?

+0

Bạn đã đúng! Cảm ơn bạn!!! – user485553

+3

Bạn vẫn có thể thực hiện một phép chọn không được kiểm soát 'E [] arr = (E []) đối tượng mới [3]; ' – Saintali

+1

@Saintali: Bạn có thể, nhưng bạn sẽ nhận được một' ClassCastException' thời điểm bạn sẽ cố gắng làm việc với mảng này. Thử nó. – NPE

2

Xét type erasure (có nghĩa là, thực tế là Generics loại thông số như E trong ví dụ của bạn được xóa tại loại biên soạn), tôi nghi ngờ các bytecode tạo ra sẽ tương tự trong cả hai trường hợp.

Từ quan điểm bảo trì, sử dụng thông số loại thay vì Object sẽ dẫn đến việc đọc mã dễ dàng hơn (vì nó sẽ giới hạn phôi). Nhưng vì API của ArrayList không bao giờ cho thấy mảng "thô" Object, tôi cho rằng nó không tạo ra bất kỳ sự khác biệt nào đối với các nhà phát triển Java đơn thuần của chúng tôi :)

4

Trước tiên, hãy nhận ra rằng kiểu thời gian thực tế của đối tượng mảng phải là Object[]. Điều này là do các mảng biết kiểu thành phần của chúng trong thời gian chạy (các kiểu mảng khác nhau thực sự là các kiểu khác nhau trong thời gian chạy), và do đó bạn cần xác định kiểu thành phần trong việc tạo mảng, nhưng đối tượng ArrayList không biết đối số kiểu của nó khi chạy.

Điều đó nói rằng, loại thời gian biên dịch của biến Ví dụ có thể được khai báo là một trong hai Object[] hoặc E[], với những ưu điểm và nhược điểm khác nhau:

Nếu nó được khai báo là Object[]:

private Object[] arr; 
// to create it: 
arr = new Object[3]; 
// to get an element: 
E get(int i) { return (E)arr[i]; } 

Các bất lợi của điều này là bạn phải đúc nó để E mỗi khi bạn lấy một cái gì đó ra khỏi nó, có nghĩa là bạn về cơ bản sử dụng nó như là một container thế hệ trước.

Nếu nó được khai báo là E[]:

private E[] arr; 
// to create it: 
arr = (E[])new Object[3]; 
// to get an element: 
E get(int i) { return arr[i]; } 

Ưu điểm của việc này là bạn không còn phải bỏ khi bạn nhận được điều ra khỏi nó - nó cung cấp loại kiểm tra về việc sử dụng của arr, giống như các thùng chứa chung. Bất lợi là, một cách hợp lý, diễn viên là nói dối - chúng tôi biết chúng tôi tạo ra một đối tượng có kiểu thời gian chạy là Object[], và vì vậy nó không phải là một ví dụ của E[], trừ khi EObject.

Tuy nhiên, không có vấn đề ngay lập tức khi thực hiện việc này, vì E bị xóa thành Object bên trong các phương pháp thể hiện của lớp học.Cách duy nhất một vấn đề có thể xảy ra là nếu đối tượng bằng cách nào đó tiếp xúc với bên ngoài của lớp (ví dụ như được trả về trong một phương thức, đặt trong trường công khai, v.v.) với công suất sử dụng loại của nó là E[] (không phải) :

// This would be bad. It would cause a class cast exception at the call site 
E[] getArray() { return arr; } 

Nhưng ArrayList, và thực sự bất kỳ lớp container đúng thiết kế tốt, sẽ không bao giờ tiếp xúc với một chi tiết thực hiện như mảng nội bộ của mình ra bên ngoài. Nó sẽ phá vỡ trừu tượng, trong số những thứ khác. Vì vậy, miễn là tác giả của lớp này nhận thức được không bao giờ phơi bày mảng này, không có vấn đề gì khi thực hiện theo cách này (có thể tiết kiệm có thể gây nhầm lẫn cho người tiếp theo nhìn thấy mã và không biết về nó), và miễn phí lợi thế của việc kiểm tra kiểu tăng lên theo cách này mang lại.

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