2012-01-06 39 views
21

Đọc mã của người khác, tôi đã nhìn thấy rất nhiều:Tại sao trước tiên chúng ta khai báo các kiểu con là siêu kiểu của chúng trước khi chúng ta khởi tạo chúng?

List<E> ints = new ArrayList<E>(); 
Map<K, V> map = new HashMap<K, V>(); 

Câu hỏi của tôi là: điểm là những gì/lợi thế của instantiating chúng theo cách đó như trái ngược với:

ArrayList<E> ints = new ArrayList<E>(); 
HashMap<K, V> map = new HashMap<K, V>(); 

gì cũng làm cho nó kỳ lạ là tôi chưa bao giờ thấy bất cứ điều gì như:

CharSequence s = new String("String"); 

hoặc

OutputStream out = new PrintStream(OutputStream); 


Bản sao (của phần đầu tiên của câu hỏi):

When/why to use/define an interface

Use interface or type for variable definition in java?

When should I use an interface in java?

why are interfaces created instead of their implementations for every class

What's the difference between these two java variable declarations?

Trả lời

24

Trả lời nhanh? Sử dụng giao diện và siêu lớp làm tăng tính di động và khả năng bảo trì mã của bạn, chủ yếu bằng cách ẩn chi tiết triển khai. Lấy ví dụ giả thuyết sau:

class Account { 
    private Collection<Transaction> transactions; 

    public Account() { 
     super(); 
     transactions = new ArrayList<Transaction>(4); 
    } 

    public Collection<Transaction> getTransactions() { 
     return transactions; 
    } 
} 

Tôi đã tuyên bố một hợp đồng cho một tài khoản mà biểu rằng các giao dịch được đưa lên tài khoản có thể được lấy ra như một bộ sưu tập.Người gọi mã của tôi không phải quan tâm đến loại bộ sưu tập mà phương thức của tôi thực sự trả về và không nên. Và điều đó giải phóng tôi để thay đổi việc thực hiện nội bộ nếu tôi cần, mà không ảnh hưởng (còn gọi là phá vỡ) số lượng khách hàng không xác định. Vì vậy, để wit, nếu tôi phát hiện ra rằng tôi cần áp đặt một số loại độc nhất trên giao dịch của mình, tôi có thể thay đổi triển khai được hiển thị ở trên từ ArrayList thành HashSet, không ảnh hưởng tiêu cực đến bất kỳ ai sử dụng lớp học của tôi.

public Account() { 
    super(); 
    transactions = new HashSet<Transaction>(4); 
} 

Theo như câu hỏi thứ hai của bạn, tôi có thể nói rằng bạn sử dụng hiệu trưởng của tính di động và đóng gói ở bất cứ đâu. Không có nhiều triển khai CharSequence khủng khiếp trên mạng, và String là phổ biến nhất được sử dụng phổ biến nhất. Vì vậy, bạn sẽ không thấy nhiều nhà phát triển khai báo biến CharSequence trong mã của họ.

+0

Chấp nhận điều này là câu trả lời vì trong khi nó cũng là kỹ lưỡng nhất bằng cách cung cấp một ví dụ thực tế, phần thứ hai của câu hỏi của tôi được trả lời tốt nhất ở đây. – chrisdotcode

7

Đối

List<E> ints = new ArrayList<E>(); 
Map<K, V> map = new HashMap<K, V>(); 

ListMap là giao diện, vì vậy bất kỳ lớp thực hiện những giao diện có thể được gán cho những tài liệu tham khảo.

ArrayList là một trong nhiều lớp (một lớp khác là LinkedList) triển khai giao diện List.

Tương tự với Map. HashMap, LinkedHashMap, TreeMap tất cả triển khai Bản đồ.

Đó là nguyên tắc chung Để lập trình cho giao diện chứ không phải để triển khai. Do đó, nhiệm vụ lập trình trở nên dễ dàng hơn. Bạn có thể tự động thay đổi hành vi của các tham chiếu.

Nếu bạn viết

ArrayList<E> ints = new ArrayList<E>(); 
HashMap<K, V> map = new HashMap<K, V>(); 

intsmap sẽ ArrayListHashMap chỉ, mãi mãi.

2

Kiểu (tốt) này tuyên bố loại là Interface việc thực hiện lớp là quan trọng vì nó buộc chúng ta sử dụng các phương thức chỉ được xác định trong Interface. Kết quả là, khi chúng ta cần thay đổi việc triển khai lớp của mình (nghĩa là chúng tôi thấy ArraySet tốt hơn tiêu chuẩn HashSet), chúng tôi đảm bảo rằng nếu chúng tôi thay đổi lớp, mã của chúng tôi sẽ hoạt động vì cả hai lớp đều thực thi nghiêm ngặt Interface.

3

Bạn làm điều này để đảm bảo sau khi làm việc với các biến bạn (hoặc bất cứ ai sử dụng các lớp học của bạn) sẽ không dựa vào các phương pháp cụ thể để thực hiện lựa chọn (ArrayList, HashMap, vv)

1

Nó chỉ là dễ nghĩ hơn về String kể từ String. Cùng với việc dễ dàng hơn (và có lợi hơn) khi nghĩ đến số WhateverList tại số List.

Tiền thưởng được thảo luận nhiều lần, nhưng tóm lại, bạn chỉ cần tách riêng các mối quan tâm: khi bạn cần CharSequence, bạn sử dụng nó. Rất khó bạn chỉ cần ArrayList: thường, mọi Danh sách sẽ thực hiện.

11

Sử dụng giao diện có lợi thế chính là sau này bạn có thể thay đổi triển khai (lớp) mà không cần thay đổi nhiều hơn một dòng mà bạn tạo cá thể và thực hiện nhiệm vụ.

4

Là nguyên tắc thiết kế mà bạn program to the interface and not to the implementation.

Bằng cách đó bạn có thể cung cấp sau một triển khai mới cho cùng một giao diện.

Từ liên kết ở trên Eric Gamma giải thích:

Nguyên tắc này thực sự là về mối quan hệ phụ thuộc mà phải được quản lý một cách cẩn thận trong một ứng dụng lớn. Thật dễ dàng để thêm sự phụ thuộc vào một lớp học. Nó gần như quá dễ dàng; chỉ cần thêm một câu lệnh nhập khẩu và các công cụ phát triển Java hiện đại như Eclipse thậm chí viết câu lệnh này cho bạn. Điều thú vị là nghịch đảo không phải là dễ dàng và loại bỏ một phụ thuộc không mong muốn có thể được tái cấu trúc thực sự làm việc hoặc thậm chí tệ hơn, ngăn cản bạn sử dụng lại mã trong ngữ cảnh khác. Vì lý do này bạn phải phát triển với đôi mắt mở khi nói đến việc giới thiệu các phụ thuộc. Nguyên tắc này cho chúng ta biết rằng tùy thuộc vào một giao diện thường có lợi.

Ở đây, termin interface đề cập không chỉ đối với vật Java, nhưng giao diện công một đối tượng nhất định có, mà về cơ bản bao gồm các phương pháp nó có, vì vậy, nó có thể là một giao diện Java (như List trong ví dụ của bạn) hoặc một siêu lớp cụ thể.

Vì vậy, trong ví dụ của bạn nếu bạn muốn sử dụng LinkedList thay vào đó sẽ khó hơn vì loại đã được khai báo là ArrayList khi danh sách chỉ vừa đủ.

Tất nhiên, nếu bạn cần các phương pháp cụ thể từ một triển khai cụ thể, bạn phải khai báo nó theo kiểu đó.

Tôi hy vọng điều này sẽ hữu ích.

1

Khi bạn tại một số điểm quyết định sử dụng một thực hiện khác nhau, nói:

List<E> ints = new LinkedList<E>(); 

thay vì

List<E> ints = new ArrayList<E>(); 

thay đổi này cần phải được thực hiện chỉ tại một nơi duy nhất.

Có số dư phù hợp để thực hiện:

thường bạn sử dụng loại cung cấp cho bạn sự đảm bảo phù hợp nhất. Rõ ràng, List cũng là Collection cũng là một cái gì đó Iterable. Tuy nhiên, bộ sưu tập không cung cấp cho bạn một yêu cầu và một bộ sưu tập không có phương thức "thêm".

Sử dụng ArrayList cho loại biến cũng hợp lý, khi bạn muốn rõ ràng hơn về nhu cầu truy cập ngẫu nhiên nhanh theo vị trí đối tượng - trong LinkedList, "get (100)" chậm hơn rất nhiều. (Nó sẽ được tốt đẹp nếu Java có một giao diện cho điều này, nhưng tôi không nghĩ rằng có một. Bằng cách sử dụng ArrayList, bạn không cho phép đúc một mảng dạng danh sách.)

1
List<E> ints = new ArrayList<E>(); 

Nếu bạn viết một số mã có giao dịch chỉ với List thì nó sẽ hoạt động đối với bất kỳ lớp nào thực hiện List (ví dụ: LinkedList, v.v.). Tuy nhiên, nếu mã của bạn trực tiếp giao dịch với ArrayList thì mã đó bị giới hạn ở ArrayList.

CharSequence s = new String("String"); 

Tạo thủ công đối tượng String không tốt. Thay vào đó, bạn nên sử dụng chuỗi ký tự. Tôi chỉ đoán lý do mà bạn không thấy CharSequence có thể bởi vì nó khá mới và cũng có thể, các chuỗi là không thay đổi.

0

Đây là chương trình để giao diện không thực hiện, theo Gang of Four. Điều này sẽ giúp ngăn chặn mã trở nên phụ thuộc vào các phương thức chỉ được thêm vào các triển khai cụ thể và giúp dễ dàng thay đổi để sử dụng triển khai khác nếu điều đó trở nên cần thiết vì bất kỳ lý do gì, ví dụ: hiệu suất.

3

Lý do đằng sau này là không kỹ thuật nhưng những thứ bạn phải đọc giữa các dòng mã: Các ListMap ví dụ nói: "Tôi chỉ quan tâm đến những thứ danh sách/bản đồ cơ bản, về cơ bản bạn có thể sử dụng bất cứ điều gì ở đây ."Một ví dụ cực đoan của đó sẽ là

Iterable<Foo> items = new ArrayList<Foo>(); 

khi bạn thực sự chỉ muốn làm một số nội dung đối với từng điều.

Là một tiền thưởng thêm này làm cho nó dễ dàng hơn một chút để cấu trúc lại các mã sau vào tiện ích chung các lớp/phương thức không yêu cầu loại bê tông Hoặc bạn có muốn mã hóa thuật toán nhiều lần cho từng loại bộ sưu tập không? bằng Java - mỗi "foo" chữ đều tự động là String và sớm hay muộn bạn để cung cấp các ký tự cho một số phương thức chỉ chấp nhận String và b) CharSequence thực sự là ahh tối thiểu. Nó thậm chí không hỗ trợ Unicode vượt quá BMP đúng và nó bỏ lỡ hầu hết các phương thức truy vấn/thao tác của String.

4

@Bhushan đã trả lời lý do. Để trả lời nhầm lẫn của bạn Tại sao không ai sử dụng

CharSequence s = new String("String"); 

hoặc

OutputStream out = new PrintStream(OutputStream); 

CharSequence chỉ chứa vài phương pháp phổ biến. Các lớp khác thực hiện giao diện này chủ yếu là bộ đệm và chỉ String là không thay đổi. CharSequence xác định api phổ biến cho các lớp được hỗ trợ bởi mảng char và Giao diện này không tinh chỉnh hợp đồng chung của phương thức equals và hashCode (xem javadoc).

OutputStream là api cấp thấp để ghi dữ liệu. Bởi vì PrintStreamthêm các phương thức tiện lợi bổ sung để viết - mức trừu tượng cao hơn, nó được sử dụng trên OutputStream.

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