2015-09-02 14 views
74

Có lý do chính đáng nào để phương pháp Collections.list() trong gói java.utils trả lại ArrayList<T> thay vì List<T>?Tại sao Enumeration được chuyển đổi thành ArrayList và không liệt kê trong java.utils?

Rõ ràng là ArrayListList, nhưng tôi theo ấn tượng rằng thực tiễn tốt là trả lại loại giao diện thay vì loại triển khai.

+0

Từ Javadoc: "_Trả lại danh sách mảng [...] _". Tại sao họ quyết định khóa API để trả về một 'ArrayList' là uinknown, và chúng ta không thể trả lời câu hỏi này, ngay cả khi nhìn vào mã nguồn của' Bộ sưu tập'. – Laf

+0

có thể là do lệnh của các enums phải được duy trì? nó được đề cập trong liên kết trong câu hỏi của bạn, _Trả lại một danh sách mảng chứa các phần tử được trả về theo kiểu liệt kê được chỉ định theo thứ tự chúng được trả về bởi enumeration_ –

+5

@ Amit.rk3 a [List] (http://docs.oracle.com /javase/7/docs/api/java/util/List.html) luôn giữ thứ tự bất kỳ việc triển khai nào bạn chọn. –

Trả lời

54

Disclaimer: Tôi không phải là một tác giả JDK.

Tôi đồng ý rằng đó là đúng để viết mã của riêng bạn đến giao diện, nhưng nếu bạn đang đi trở lại một mutable bộ sưu tập cho một bên thứ ba, điều quan trọng là để cho các bên thứ ba biết những gì sắp xếp của List họ đang trở lại.

LinkedListArrayList rất khác nhau, hiệu suất khôn ngoan, cho các hoạt động khác nhau. Ví dụ: xóa phần tử đầu tiên của ArrayListO(n), nhưng loại bỏ phần tử đầu tiên của LinkedListO(1).

Bằng cách chỉ định đầy đủ kiểu trả về, các tác giả JDK là trao đổi thông tin bổ sung, trong mã rõ ràng về loại đối tượng mà chúng cung cấp cho bạn, để bạn có thể viết mã của mình để sử dụng phương pháp này đúng cách. Nếu bạn thực sự cần LinkedList, bạn biết rằng bạn phải chỉ định một tài khoản tại đây.

Cuối cùng, lý do chính để mã hóa giao diện trong quá trình triển khai là nếu bạn nghĩ rằng việc triển khai sẽ thay đổi. Các tác giả JDK có thể cho rằng họ là không bao giờ sẽ thay đổi phương thức này; nó sẽ không bao giờ trả lại một số LinkedList hoặc Collections.UnmodifiableList. Tuy nhiên, trong hầu hết các trường hợp, bạn có thể vẫn sẽ làm:

List<T> list = Collections.list(enumeration); 
+19

Dường như đây là một loại Luật thiết kế API của Postel: nói chung với các yếu tố đầu vào của bạn, cụ thể với các đầu ra của bạn. –

+2

Một ngày nào đó họ sẽ thêm phương thức 'toLinkedList', và sau đó tên' list' cho phiên bản ArrayList sẽ trông ngớ ngẩn. – immibis

+1

Tôi nghĩ thông tin quan trọng ở đây là danh sách có thể thay đổi hay không. Ví dụ. phương thức nào của Giao diện thực hiện ném một UnsupportedOperationException và cái nào thì không. – keuleJ

31

Khi trả lại List, bạn sẽ quảng bá chương trình đến giao diện, một phương pháp rất hay. Tuy nhiên, cách tiếp cận này có giới hạn của nó. Ví dụ: bạn không thể sử dụng một số phương thức được xác định cho ArrayList và không tồn tại trong giao diện List - Xem this answer để biết chi tiết.

tôi trích dẫn API Design từ Java ™ Hướng dẫn:

... Đó là tốt để trả lại một đối tượng của bất kỳ loại rằng thực hiện hoặc kéo dài một trong những giao diện bộ sưu tập . Đây có thể là một trong các giao diện hoặc một loại mục đích đặc biệt mở rộng hoặc thực hiện một trong các giao diện này.

.. Trong một ý nghĩa, giá trị trả lại nên có những hành vi trái ngược với thông số đầu vào: Đó là tốt nhất trở lại giao diện thu thập được áp dụng cụ thể nhất chứ không phải là chung nhất. Ví dụ: nếu bạn chắc chắn rằng bạn sẽ luôn trả về SortedMap, bạn nên cung cấp phương thức có liên quan loại trả về SortedMap thay vì Map. SortedMap trường hợp tốn nhiều thời gian hơn để xây dựng hơn các trường hợp Map thông thường và cũng mạnh hơn. Cho rằng mô-đun của bạn đã đầu tư thời gian để xây dựng một SortedMap, nó làm cho cảm giác tốt để cung cấp cho người dùng quyền truy cập của nó tăng lên. Hơn nữa, người dùng sẽ có thể truyền đối tượng trả về cho các phương thức yêu cầu SortedMap, cũng như những phương thức chấp nhận bất kỳ Map nào.

Vì bản chất ArrayList là một mảng, chúng là lựa chọn đầu tiên của tôi khi tôi cần có "mảng thu thập". Vì vậy, nếu tôi muốn chuyển đổi liệt kê thành một danh sách, lựa chọn của tôi sẽ là một danh sách mảng.

Trong bất kỳ trường hợp khác, nó vẫn còn hợp lệ để viết:

List<T> list = Collections.list(e); 
+4

Về "giao diện bộ sưu tập áp dụng cụ thể nhất thay vì giao diện chung nhất", điều đó có ý nghĩa, nhưng ArrayList không phải là giao diện. Trong trường hợp này, Danh sách có vẻ là giao diện cụ thể nhất. –

+2

Trong trường hợp của một 'SortedMap', nó thêm chức năng cho một' Map' bình thường, vì vậy tôi nghĩ rằng nó có ý nghĩa để xác định nó như là một kiểu trả về. Tuy nhiên, một 'ArrayList' không thêm gì vào một' List', và có thể được hoán đổi với một 'LinkedList' và bạn sẽ không thấy bất cứ điều gì khác biệt (chức năng khôn ngoan, không hiệu quả). Tuy nhiên, tôi nghĩ rằng bạn tham khảo là rất thú vị. +1. – Laf

+1

@Laf thực sự có nhiều điều cần cân nhắc khi chọn 'LinkedList' hoặc' ArrayList'. Giống như nhận được thời gian phức tạp .. – Maroun

6

Chức năng trả về "quyền sở hữu độc quyền" của các đối tượng có thể thay đổi mới được tạo nên thường là loại thực tế nhất; những thứ trả về các đối tượng bất biến, đặc biệt nếu chúng có thể được chia sẻ, thường sẽ trả về các loại ít cụ thể hơn.

Lý do để phân biệt là trong trường hợp trước đây, một đối tượng sẽ luôn có thể tạo ra một đối tượng mới của loại được chỉ định và vì người nhận sẽ sở hữu đối tượng và không cho biết hành động của người nhận để thực hiện, nói chung sẽ không có cách nào để mã trả về đối tượng để biết liệu có bất kỳ triển khai giao diện thay thế nào có thể đáp ứng nhu cầu của người nhận hay không.

Trong trường hợp thứ hai, thực tế là đối tượng không thay đổi có nghĩa là hàm có thể xác định loại thay thế có thể làm mọi thứ phức tạp hơn có thể làm cho nội dung chính xác. Ví dụ: giao diện Immutable2dMatrix có thể được triển khai bởi lớp ImmutableArrayBacked2dMatrix và lớp ImmutableDiagonal2dMatrix. Một hàm có nghĩa vụ trả về một hình vuông Immutable2dMatrix có thể quyết định trả lại một phiên bản ImmutableDiagonalMatrix nếu tất cả các phần tử nằm ngoài đường chéo chính xảy ra bằng 0 hoặc ImmutableArrayBackedMatrix nếu không. Loại cũ sẽ mất không gian lưu trữ ít hơn nhiều, nhưng người nhận không nên quan tâm đến sự khác biệt giữa chúng.

Trả lại Immutable2dMatrix thay vì một bê tông ImmutableArrayBackedMatrix cho phép mã để chọn loại trả về dựa trên những gì mảng chứa; nó cũng có nghĩa là nếu mã được cho là sẽ trả về mảng xảy ra là đang nắm giữ việc thực hiện phù hợp Immutable2dMatrix nó có thể đơn giản trả về điều đó, thay vì phải xây dựng một cá thể mới. Cả hai yếu tố này có thể là "thắng" lớn khi làm việc với các vật thể bất biến. Tuy nhiên,

Tuy nhiên, khi làm việc với các đối tượng có thể thay đổi được, cả hai yếu tố đều không được phát. Thực tế là một mảng có thể thay đổi có thể không có bất kỳ phần tử nào trên đường chéo chính khi nó được tạo ra không có nghĩa là nó sẽ không bao giờ có bất kỳ phần tử nào như vậy. Do đó, trong khi ImmutableDiagonalMatrix là một loại phụ có hiệu quả là Immutable2dMatrix, một số MutableDiagonalMatrix không phải là loại phụ của một số Mutable2dMatrix, vì sau này có thể chấp nhận các cửa hàng ở đường chéo chính trong khi trường hợp cũ không thể. Hơn nữa, trong khi các đối tượng bất biến thường có thể và nên được chia sẻ, các đối tượng có thể thay đổi thường không thể. Một hàm được yêu cầu cho bộ sưu tập có thể thay đổi mới được khởi tạo với nội dung nhất định sẽ cần tạo bộ sưu tập mới cho dù cửa hàng sao lưu có khớp với loại được yêu cầu hay không.

1

Có một chi tiết nhỏ trên phương thức gọi var một giao diện thay vì trực tiếp trên đối tượng.

Chi phí này thường không quá 1 hoặc 2 hướng dẫn của bộ xử lý. Chi phí gọi phương thức thậm chí còn thấp hơn nếu JIT biết rằng phương thức là cuối cùng.Điều này không thể đo lường được đối với hầu hết mã bạn và tôi, nhưng đối với các phương thức mức thấp trong java.utils có thể được sử dụng trong một số mã mà nó là một vấn đề.

Cũng như đã được chỉ ra trong các câu trả lời khác, loại cụ thể của đối tượng trả về (ngay cả khi bị ẩn sau giao diện) sẽ ảnh hưởng đến hiệu suất của mã sử dụng nó. Sự thay đổi về hiệu suất này có thể rất lớn, do đó mức độ mà phần mềm gọi không hoạt động.

Rõ ràng các tác giả của java.utils không có cách nào để biết tất cả phần mềm gọi Collections.list() có kết quả và không có cách nào để kiểm tra lại phần mềm này nếu chúng thay đổi việc cấy ghép Collections.list(). Vì vậy, họ sẽ không thay đổi việc cấy ghép Collections.list() để trả về một loại Danh sách khác, ngay cả khi hệ thống kiểu cho phép nó!

Khi viết phần mềm của riêng bạn, bạn (hy vọng) có kiểm tra tự động bao gồm tất cả mã của bạn và hiểu rõ cách mã của bạn tương tác bao gồm biết hiệu suất là vấn đề ở đâu. Có thể thay đổi phương thức, mà không phải thay đổi người gọi có giá trị lớn trong khi thiết kế của phần mềm đang thay đổi.

Do đó, hai tập hợp thương mại rất khác nhau.

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