Điều gì là thú vị về generic, tại sao lại sử dụng chúng?


87

Tôi nghĩ tôi sẽ tặng quả bóng mềm này cho bất kỳ ai muốn đánh nó ra khỏi công viên. Thuốc generic là gì, ưu điểm của thuốc generic là gì, tại sao, ở đâu, tôi nên sử dụng chúng như thế nào? Hãy giữ nó khá cơ bản. Cảm ơn.


1
Bản sao của stackoverflow.com/questions/77632/… . Nhưng câu trả lời đơn giản, ngay cả khi các bộ sưu tập không phải là một phần của API của bạn, tôi không thích truyền không cần thiết ngay cả trong triển khai nội bộ.
Matthew Flaschen

Câu hỏi khá giống nhau, nhưng tôi không nghĩ câu trả lời được chấp nhận trả lời câu hỏi này.
Tom Hawtin - tackline

1
Ngoài ra, hãy kiểm tra stackoverflow.com/questions/520527
Clint Miller

4
@MatthewFlaschen kỳ lạ, bản sao của bạn hướng đến câu hỏi này ... trục trặc trong ma trận!
jcollum

@jcollum, tôi nghĩ câu hỏi ban đầu mà tôi đăng nhận xét đó đã được hợp nhất với câu hỏi này.
Matthew Flaschen

Câu trả lời:


126
  • Cho phép bạn viết mã / sử dụng các phương thức thư viện an toàn về kiểu chữ, tức là một Danh sách <chuỗi> được đảm bảo là một danh sách các chuỗi.
  • Do các generic được sử dụng, trình biên dịch có thể thực hiện kiểm tra thời gian biên dịch đối với mã để đảm bảo an toàn kiểu, tức là bạn có đang cố gắng đặt một số int vào danh sách chuỗi đó không? Sử dụng ArrayList sẽ gây ra lỗi thời gian chạy kém minh bạch hơn.
  • Nhanh hơn so với việc sử dụng các đối tượng vì nó tránh được quyền chọn / mở hộp (trong đó .net phải chuyển đổi kiểu giá trị thành kiểu tham chiếu hoặc ngược lại ) hoặc truyền từ đối tượng sang kiểu tham chiếu bắt buộc.
  • Cho phép bạn viết mã có thể áp dụng cho nhiều kiểu có cùng hành vi cơ bản, tức là Từ điển <string, int> sử dụng cùng mã cơ bản như Từ điển <DateTime, double>; bằng cách sử dụng generic, nhóm framework chỉ phải viết một đoạn mã để đạt được cả hai kết quả với những lợi thế đã nói ở trên.

9
Ah, rất hay, và tôi thích danh sách gạch đầu dòng. Tôi nghĩ đây là câu trả lời ngắn gọn nhưng đầy đủ nhất cho đến nay.
MrBoJangle 16-08

toàn bộ lời giải thích nằm trong ngữ cảnh "bộ sưu tập". tôi đang tìm kiếm cái nhìn rộng hơn. bất kỳ suy nghĩ?
forest_mole

câu trả lời hay, tôi chỉ muốn thêm 2 ưu điểm khác, hạn chế chung có 2 lợi ích bên cạnh 1- Bạn có thể sử dụng các thuộc tính của kiểu bị ràng buộc trong kiểu chung (ví dụ: trong đó T: ICompABLE cung cấp để sử dụng CompareTo) 2- Người sẽ viết mã sau khi bạn sẽ biết những gì sẽ làm
Hamit YILDIRIM

52

Tôi thực sự ghét phải lặp lại chính mình. Tôi ghét gõ cùng một thứ thường xuyên hơn tôi phải làm. Tôi không thích lặp lại mọi thứ nhiều lần với những khác biệt nhỏ.

Thay vì tạo:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

Tôi có thể tạo một lớp có thể tái sử dụng ... (trong trường hợp bạn không muốn sử dụng bộ sưu tập thô vì lý do nào đó)

class MyList<T> {
   T get(int index) { ... }
}

Bây giờ tôi hiệu quả hơn gấp 3 lần và tôi chỉ phải duy trì một bản sao. Tại sao bạn KHÔNG muốn duy trì mã ít hơn?

Điều này cũng đúng đối với các lớp không phải là tập hợp như a Callable<T>hoặc a Reference<T>phải tương tác với các lớp khác. Bạn có thực sự muốn mở rộng Callable<T>Future<T>mọi lớp liên quan khác để tạo các phiên bản an toàn kiểu không?

Tôi không.


1
Tôi không chắc là tôi hiểu. Tôi không đề nghị bạn tạo trình bao bọc "MyObject", điều đó sẽ thật kinh khủng. Tôi gợi ý rằng nếu bộ sưu tập đối tượng của bạn là bộ sưu tập Ô tô, thì bạn viết đối tượng Nhà để xe. Nó sẽ không có (xe hơi), nó sẽ có các phương thức như buyFuel (100) sẽ mua 100 đô la nhiên liệu và phân phối nó cho những chiếc xe cần nó nhất. Tôi đang nói về các lớp học kinh doanh thực sự, không chỉ là "lớp học". Ví dụ, bạn hầu như không bao giờ được lấy (xe hơi) hoặc lặp lại chúng bên ngoài bộ sưu tập - bạn không yêu cầu Đối tượng làm thành viên của nó, thay vào đó bạn yêu cầu nó thực hiện một thao tác cho bạn.
Bill K

Ngay cả trong nhà để xe của bạn, bạn nên có loại an toàn. Vì vậy, bạn có List <Cars>, ListOfCars hoặc bạn truyền ở mọi nơi. Tôi không cảm thấy cần phải nêu rõ loại nhiều hơn số lần tối thiểu cần thiết.
James Schek

Tôi đồng ý rằng loại an toàn sẽ tốt, nhưng nó ít hữu ích hơn nhiều vì nó chỉ giới hạn trong lớp ga ra. Sau đó, nó được đóng gói và dễ dàng kiểm soát, vì vậy quan điểm của tôi là nó mang lại cho bạn lợi thế nhỏ này với chi phí là cú pháp mới. Nó giống như việc sửa đổi hiến pháp Hoa Kỳ để làm cho việc vượt đèn đỏ trở nên bất hợp pháp - Đúng vậy, nó luôn luôn là bất hợp pháp nhưng nó có biện minh cho việc sửa đổi không? Tôi cũng nghĩ rằng nó khuyến khích mọi người KHÔNG sử dụng tính năng đóng gói cho trường hợp này, điều này thực sự tương đối có hại.
Bill K

Bill - bạn có thể có quan điểm về việc không sử dụng tính năng đóng gói, nhưng phần còn lại của lập luận của bạn là một so sánh kém. Chi phí học cú pháp mới là tối thiểu trong trường hợp này (so sánh điều này với lambdas trong C #); so sánh điều này với việc sửa đổi Hiến pháp không có ý nghĩa gì. Hiến pháp không có ý định chỉ định mã giao thông. Nó giống như chuyển sang một phiên bản in Serif-Font trên bảng áp phích thay vì một bản viết tay trên giấy da. Nó có cùng nghĩa, nhưng rõ ràng và dễ đọc hơn rất nhiều.
James Schek

Ví dụ làm cho khái niệm thực tế hơn. Cảm ơn vì điều đó.
RayLoveless

22

Không cần đánh máy là một trong những lợi thế lớn nhất của Java generic , vì nó sẽ thực hiện kiểm tra kiểu tại thời điểm biên dịch. Điều này sẽ làm giảm khả năng ClassCastExceptions có thể được ném trong thời gian chạy và có thể dẫn đến mã mạnh hơn.

Nhưng tôi nghi ngờ rằng bạn hoàn toàn nhận thức được điều đó.

Mỗi khi tôi nhìn vào Generics, nó khiến tôi đau đầu. Tôi thấy phần tốt nhất của Java là sự đơn giản và cú pháp tối thiểu và tổng thể không đơn giản và thêm một lượng lớn cú pháp mới.

Lúc đầu, tôi cũng không thấy lợi ích của thuốc generic. Tôi bắt đầu học Java từ cú pháp 1.4 (mặc dù Java 5 đã ra mắt vào thời điểm đó) và khi tôi gặp phải các khái niệm chung, tôi cảm thấy viết nhiều mã hơn và tôi thực sự không hiểu lợi ích của nó.

Các IDE hiện đại làm cho việc viết mã bằng generic dễ dàng hơn.

Hầu hết các IDE hiện đại, tốt đều đủ thông minh để hỗ trợ viết mã bằng các mã chung, đặc biệt là khi hoàn thành mã.

Đây là một ví dụ về cách tạo Map<String, Integer>với a HashMap. Mã tôi sẽ phải nhập là:

Map<String, Integer> m = new HashMap<String, Integer>();

Và thực sự, có rất nhiều thứ để nhập chỉ để tạo ra một cái mới HashMap. Tuy nhiên, trên thực tế, tôi chỉ phải nhập nhiều điều này trước khi Eclipse biết tôi cần gì:

Map<String, Integer> m = new Ha Ctrl+Space

Đúng, tôi cần phải chọn HashMaptừ danh sách các ứng cử viên, nhưng về cơ bản IDE biết phải thêm những gì, bao gồm cả các loại chung chung. Với các công cụ phù hợp, việc sử dụng generic không quá tệ.

Ngoài ra, vì các kiểu đã biết nên khi truy xuất các phần tử từ tập hợp chung, IDE sẽ hoạt động như thể đối tượng đó đã là một đối tượng thuộc kiểu được khai báo của nó - không cần ép kiểu để IDE biết kiểu của đối tượng. Là.

Lợi thế chính của generics đến từ cách nó hoạt động tốt với các tính năng Java 5 mới. Đây là một ví dụ về việc chuyển các số nguyên vào a Setvà tính tổng của nó:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

Trong đoạn mã đó, có ba tính năng mới của Java 5:

Đầu tiên, generic và autoboxing các nguyên thủy cho phép các dòng sau:

set.add(10);
set.add(42);

Số nguyên 10được tự động đóng hộp thành một Integervới giá trị là 10. (Và tương tự cho 42). Sau đó, nó Integerđược ném vào Setcái được biết là giữ Integers. Cố gắng ném vào một Stringsẽ gây ra lỗi biên dịch.

Tiếp theo, vòng lặp for-each lấy cả ba điều sau:

for (int i : set) {
  total += i;
}

Đầu tiên, các s Setchứa Integerđược sử dụng trong vòng lặp for-each. Mỗi phần tử được khai báo là một intvà được cho phép khi phần tử Integerđược mở hộp trở lại nguyên thủy int. Và thực tế là việc mở hộp này xảy ra được biết đến bởi vì generic đã được sử dụng để chỉ định rằng có những thứ được Integergiữ trong Set.

Generics có thể là chất kết dính kết hợp các tính năng mới được giới thiệu trong Java 5 và nó chỉ làm cho việc viết mã trở nên đơn giản và an toàn hơn. Và hầu hết thời gian IDE đủ thông minh để giúp bạn đưa ra các đề xuất tốt, vì vậy nói chung, bạn sẽ không phải nhập nhiều hơn.

Và thành thật mà nói, như có thể thấy từ Setví dụ, tôi cảm thấy rằng việc sử dụng các tính năng của Java 5 có thể làm cho mã ngắn gọn và mạnh mẽ hơn.

Chỉnh sửa - Một ví dụ không có số liệu chung

Sau đây là minh họa của Setví dụ trên mà không cần sử dụng thuốc generic. Có thể, nhưng không hẳn là dễ chịu:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(Lưu ý: Đoạn mã trên sẽ tạo ra cảnh báo chuyển đổi không được kiểm tra tại thời điểm biên dịch.)

Khi sử dụng các bộ sưu tập không phải generics, các kiểu được nhập vào bộ sưu tập là các đối tượng của kiểu Object. Do đó, trong ví dụ này, a Objectlà những gì đang được addđưa vào tập hợp.

set.add(10);
set.add(42);

Trong các dòng trên, autoboxing đang được phát - intgiá trị nguyên thủy 1042đang được autoboxed thành Integercác đối tượng, đang được thêm vào Set. Tuy nhiên, hãy nhớ rằng, các Integerđối tượng đang được xử lý dưới dạng Objects, vì không có thông tin về kiểu để giúp trình biên dịch biết kiểu nào Setnên mong đợi.

for (Object o : set) {

Đây là phần rất quan trọng. Lý do cho-mỗi vòng lặp làm việc là do Setthực hiện các Iterablegiao diện, mà trả về một Iteratorvới loại thông tin, nếu có. ( Iterator<T>, đó là.)

Tuy nhiên, vì không có thông tin kiểu, nên Setsẽ trả về an Iteratorsẽ trả về các giá trị Setdưới dạng Objects, và đó là lý do tại sao phần tử được truy xuất trong vòng lặp for-each phải là kiểu Object.

Bây giờ, khi Objectđược truy xuất từ Set, nó cần được truyền đến một Integerthủ công để thực hiện thêm:

  total += (Integer)o;

Ở đây, một typecast được thực hiện từ an Objectđến an Integer. Trong trường hợp này, chúng tôi biết điều này sẽ luôn hoạt động, nhưng việc đánh máy thủ công luôn khiến tôi cảm thấy rằng đó là mã mỏng manh có thể bị hỏng nếu một thay đổi nhỏ được thực hiện ở nơi khác. (Tôi cảm thấy rằng mỗi lần đánh máy là một sự ClassCastExceptionchờ đợi để xảy ra, nhưng tôi lạc đề ...)

Các Integerhiện đang mở hộp thành intvà được phép thực hiện việc bổ sung vào intbiến total.

Tôi hy vọng mình có thể minh họa rằng các tính năng mới của Java 5 có thể sử dụng với mã không chung chung, nhưng nó không rõ ràng và dễ hiểu như viết mã bằng mã chung. Và, theo ý kiến ​​của tôi, để tận dụng đầy đủ các tính năng mới trong Java 5, người ta nên xem xét các generic, nếu ít nhất, cho phép kiểm tra thời gian biên dịch để ngăn chặn các typecast không hợp lệ tạo ra các ngoại lệ trong thời gian chạy.


Việc tích hợp với vòng lặp for nâng cao thực sự rất tốt (mặc dù tôi nghĩ rằng vòng lặp chết tiệt bị hỏng vì tôi không thể sử dụng cùng một cú pháp chính xác với một tập hợp không chung chung - nó chỉ nên sử dụng cast / autobox thành int, nó có tất cả thông tin nó cần!), nhưng bạn nói đúng, sự trợ giúp trong IDE có lẽ là tốt. Tôi vẫn không biết liệu nó có đủ để biện minh cho cú pháp bổ sung hay không, nhưng ít nhất đó là thứ mà tôi có thể hưởng lợi từ đó (trả lời câu hỏi của tôi).
Bill K

@Bill K: Tôi đã thêm một ví dụ về việc sử dụng các tính năng của Java 5 mà không cần sử dụng chung.
coobird

Đó là một điểm tốt, tôi đã không sử dụng nó (tôi đã đề cập rằng tôi đã bị mắc kẹt, vào ngày 1.4 và sớm trong nhiều năm nay). Tôi đồng ý rằng có những lợi thế, nhưng kết luận mà tôi đạt được là A) chúng có xu hướng khá thuận lợi đối với những người không sử dụng OO đúng cách và ít được sử dụng hơn đối với những người có, và B) Những lợi thế của bạn ' được liệt kê là những lợi thế, nhưng hầu như không đủ giá trị để biện minh cho dù chỉ là một thay đổi nhỏ về cú pháp, chứ chưa nói đến những thay đổi lớn cần thiết để thực sự tìm hiểu về generic như được triển khai trong Java. Nhưng ít nhất là có lợi thế.
Bill K

15

Nếu bạn đã tìm kiếm cơ sở dữ liệu lỗi Java ngay trước 1.5 được phát hành, bạn muốn tìm gấp bảy lần nữa lỗi với NullPointerExceptionhơn ClassCastException. Vì vậy, có vẻ như đây không phải là một tính năng tuyệt vời để tìm lỗi, hoặc ít nhất là lỗi vẫn tồn tại sau một thử nghiệm nhỏ.

Đối với tôi, lợi thế lớn của generic là chúng ghi lại thông tin loại quan trọng trong mã . Nếu tôi không muốn thông tin kiểu đó được ghi lại trong mã, thì tôi sẽ sử dụng ngôn ngữ được nhập động hoặc ít nhất là ngôn ngữ có suy luận kiểu ẩn hơn.

Giữ các bộ sưu tập của một đối tượng cho riêng nó không phải là một phong cách tồi (nhưng sau đó, phong cách phổ biến là bỏ qua một cách hiệu quả tính năng đóng gói). Nó phụ thuộc vào những gì bạn đang làm. Việc chuyển các bộ sưu tập đến "thuật toán" sẽ dễ dàng hơn một chút để kiểm tra (tại hoặc trước thời điểm biên dịch) với generic.


6
Nó không chỉ được ghi lại (bản thân nó đã là một chiến thắng lớn) mà còn được thực thi bởi trình biên dịch.
James Schek

Điểm tốt, nhưng điều đó cũng không được hoàn thành bằng cách đặt bộ sưu tập trong một lớp xác định "bộ sưu tập của loại đối tượng này"?
Bill K

1
James: Vâng, đó là điểm về nó được ghi lại trong mã .
Tom Hawtin - tackline

Tôi không nghĩ rằng tôi biết về các thư viện tốt sử dụng bộ sưu tập cho "Thuật toán" (có thể ngụ ý rằng bạn đang nói về "Hàm" khiến tôi tin rằng chúng phải là một phương thức trong đối tượng của bộ sưu tập). Tôi nghĩ rằng JDK luôn luôn trích xuất một mảng đẹp, sạch sẽ là bản sao của dữ liệu để nó không thể bị nhầm lẫn - ít nhất là thường xuyên hơn không.
Bill K

Ngoài ra, không có một đối tượng mô tả chính xác cách bộ sưu tập được sử dụng và giới hạn nó sử dụng một dạng tài liệu trong mã tốt hơn NHIỀU? Tôi thấy thông tin được đưa ra trong generic cực kỳ dư thừa trong mã tốt.
Bill K

11

Generics trong Java tạo điều kiện cho tính đa hình tham số . Bằng các tham số kiểu, bạn có thể truyền các đối số cho các kiểu. Cũng giống như một phương thức giống như String foo(String s)mô hình một số hành vi, không chỉ cho một chuỗi cụ thể mà cho bất kỳ chuỗi nào s, vì vậy một kiểu giống như List<T>mô hình một số hành vi, không chỉ cho một kiểu cụ thể mà cho bất kỳ kiểu nào . List<T>nói rằng đối với bất kỳ loại nào T, có một loại Listcó các phần tử là Ts . Vì vậy, Listmột thực sự là một phương thức tạo kiểu . Nó nhận một kiểu làm đối số và kết quả là tạo một kiểu khác.

Dưới đây là một vài ví dụ về các loại chung mà tôi sử dụng hàng ngày. Đầu tiên, một giao diện chung rất hữu ích:

public interface F<A, B> {
  public B f(A a);
}

Giao diện này cho biết rằng đối với một số loại có hai kiểu, ABcó một hàm (được gọi f) nhận một Avà trả về a B. Khi bạn triển khai giao diện này, ABcó thể là bất kỳ kiểu nào bạn muốn, miễn là bạn cung cấp một hàm flấy cái trước và trả về cái sau. Đây là một ví dụ về triển khai giao diện:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Trước khi có generic, tính đa hình đã đạt được bằng cách phân lớp con sử dụng extendstừ khóa. Với generic, chúng ta thực sự có thể loại bỏ phân lớp con và thay vào đó sử dụng tính đa hình tham số. Ví dụ: hãy xem xét một lớp được tham số hóa (chung chung) được sử dụng để tính toán mã băm cho bất kỳ loại nào. Thay vì ghi đè Object.hashCode (), chúng ta sẽ sử dụng một lớp chung như thế này:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

Điều này linh hoạt hơn nhiều so với việc sử dụng kế thừa, bởi vì chúng ta có thể tiếp tục với chủ đề của việc sử dụng bố cục và đa hình tham số mà không khóa các cấu trúc phân cấp giòn.

Mặc dù vậy, tổng thể của Java không hoàn hảo. Bạn có thể trừu tượng hóa trên các kiểu, nhưng bạn không thể trừu tượng hóa các hàm tạo kiểu. Nghĩa là, bạn có thể nói "cho bất kỳ kiểu T nào", nhưng bạn không thể nói "cho bất kỳ kiểu T nào nhận tham số kiểu A".

Tôi đã viết một bài báo về những giới hạn này của Java generic, tại đây.

Một chiến thắng lớn với generic là chúng cho phép bạn tránh phân loại con. Phân lớp con có xu hướng dẫn đến hệ thống phân cấp lớp giòn mà khó mở rộng và các lớp khó hiểu riêng lẻ nếu không nhìn vào toàn bộ hệ thống phân cấp.

Wereas trước Generics bạn có thể có các lớp học như Widgetmở rộng FooWidget, BarWidgetBazWidget, với Generics bạn có thể có một lớp generic đơn Widget<A>mà phải mất một Foo, Barhoặc Baztrong constructor của nó để cung cấp cho bạn Widget<Foo>, Widget<Bar>Widget<Baz>.


Sẽ là một điểm tốt về phân lớp nếu Java không có giao diện. Sẽ không đúng nếu FooWidget, BarWidtet và BazWidget đều triển khai Widget? Tôi có thể sai về điều này mặc dù .. Nhưng đây là một điểm thực sự tốt nếu tôi sai.
Bill K

Tại sao bạn cần một lớp chung động để tính giá trị băm? Một hàm tĩnh với các tham số thích hợp sẽ không hoạt động hiệu quả hơn?
Ant_222

8

Generics tránh hiệu suất đánh quyền anh và unboxing. Về cơ bản, hãy xem ArrayList và List <T>. Cả hai đều làm những điều cốt lõi giống nhau, nhưng List <T> sẽ nhanh hơn rất nhiều vì bạn không phải đóng hộp đến / từ đối tượng.


Đó là bản chất của nó, phải không? Đẹp.
MrBoJangle 16/09/08

Cũng không hoàn toàn; chúng hữu ích cho việc đảm bảo an toàn cho kiểu chữ + tránh phôi cho các kiểu tham chiếu mà không cần đến quyền anh / unboxing.
ljs 16/09/08

Vì vậy, tôi đoán câu hỏi tiếp theo là, "Tại sao tôi muốn sử dụng ArrayList?" Và, chờ đợi một câu trả lời, tôi muốn nói rằng ArrayLists là tốt miễn là bạn không mong đợi đóng hộp và mở hộp các giá trị của chúng nhiều. Bình luận?
MrBoJangle 16/09/08

Không, chúng không còn tồn tại kể từ .net 2; chỉ hữu ích cho khả năng tương thích ngược. ArrayLists chậm hơn, không phải loại an toàn và yêu cầu quyền anh / unboxing hoặc truyền.
ljs 16/09/08

Nếu bạn không lưu trữ các kiểu giá trị (ví dụ: int hoặc struct), thì không có quyền nào khi sử dụng ArrayList - các lớp đã là 'đối tượng'. Sử dụng List <T> vẫn tốt hơn nhiều vì kiểu an toàn và nếu bạn thực sự muốn lưu trữ các đối tượng, thì sử dụng List <object> sẽ tốt hơn.
Wilka 16-08

6

Lợi ích tốt nhất đối với Generics là sử dụng lại mã. Giả sử rằng bạn có rất nhiều đối tượng kinh doanh và bạn sẽ viết RẤT giống mã cho mỗi đối tượng để thực hiện các hành động giống nhau. (IE Linq tới các phép toán SQL).

Với generics, bạn có thể tạo một lớp có thể hoạt động với bất kỳ kiểu nào kế thừa từ một lớp cơ sở nhất định hoặc triển khai một giao diện nhất định như sau:

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

Lưu ý việc sử dụng lại cùng một dịch vụ với các Loại khác nhau trong phương thức DoSomething ở trên. Quả thật là tao nhã!

Có nhiều lý do tuyệt vời khác để sử dụng generic cho công việc của bạn, đây là lý do yêu thích của tôi.


5

Tôi chỉ thích chúng vì chúng cung cấp cho bạn một cách nhanh chóng để xác định một loại tùy chỉnh (vì tôi vẫn sử dụng chúng).

Vì vậy, ví dụ: thay vì xác định một cấu trúc bao gồm một chuỗi và một số nguyên, và sau đó phải triển khai toàn bộ tập hợp các đối tượng và phương thức về cách truy cập một mảng của các cấu trúc đó, v.v., bạn có thể tạo một Từ điển.

Dictionary<int, string> dictionary = new Dictionary<int, string>();

Và trình biên dịch / IDE thực hiện phần còn lại của công việc nặng nhọc. Đặc biệt, một Từ điển cho phép bạn sử dụng kiểu đầu tiên làm khóa (không có giá trị lặp lại).


5
  • Các bộ sưu tập đã đánh máy - ngay cả khi bạn không muốn sử dụng chúng, bạn có thể phải xử lý chúng từ các thư viện khác, các nguồn khác.

  • Nhập chung trong tạo lớp:

    public class Foo <T> {public T get () ...

  • Tránh casting - tôi luôn không thích những thứ như

    new Comparator {public int so sánhTo (Object o) {if (o instanceof classIcareAbout) ...

Về cơ bản bạn đang kiểm tra một điều kiện chỉ nên tồn tại vì giao diện được thể hiện dưới dạng các đối tượng.

Phản ứng ban đầu của tôi đối với thuốc chung tương tự như của bạn - "quá lộn xộn, quá phức tạp". Kinh nghiệm của tôi là sau khi sử dụng chúng một chút, bạn sẽ quen với chúng, và mã mà không có chúng cảm thấy ít được chỉ định rõ ràng hơn và ít thoải mái hơn. Bên cạnh đó, phần còn lại của thế giới java sử dụng chúng nên cuối cùng bạn sẽ phải làm quen với chương trình, phải không?


1
để chính xác về việc tránh truyền: theo tài liệu của Sun, quá trình truyền xảy ra, nhưng nó được thực hiện bởi trình biên dịch. Bạn chỉ cần tránh viết phôi trong mã của mình.
Demi

Có lẽ, tôi dường như bị mắc kẹt trong một chuỗi dài các công ty không muốn vượt lên trên 1.4, vì vậy ai biết được, tôi có thể nghỉ hưu trước khi lên 5. Gần đây, tôi cũng đã nghĩ rằng tạo ra một "SimpleJava" có thể là một khái niệm thú vị --Java sẽ tiếp tục bổ sung các tính năng và đối với rất nhiều mã mà tôi đã thấy, đó sẽ không phải là một điều lành mạnh. Tôi đã từng đối phó với những người không thể viết mã một vòng lặp - Tôi không muốn thấy họ cố gắng đưa các chỉ số chung vào các vòng lặp không được cuộn một cách vô nghĩa của họ.
Bill K

@Bill K: Ít người cần sử dụng toàn bộ sức mạnh của thuốc generic. Điều đó chỉ cần thiết khi bạn đang viết một lớp mà chính nó là chung chung.
Eddie

5

Để đưa ra một ví dụ điển hình. Hãy tưởng tượng bạn có một lớp học tên là Foo

public class Foo
{
   public string Bar() { return "Bar"; }
}

Ví dụ 1 Bây giờ bạn muốn có một bộ sưu tập các đối tượng Foo. Bạn có hai tùy chọn, LIst hoặc ArrayList, cả hai đều hoạt động theo cách tương tự.

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

Trong đoạn mã trên, nếu tôi cố gắng thêm một lớp FireTruck thay vì Foo, ArrayList sẽ thêm nó, nhưng Danh sách chung của Foo sẽ khiến một ngoại lệ được ném ra.

Ví dụ hai.

Bây giờ bạn có hai danh sách mảng và bạn muốn gọi hàm Bar () trên mỗi danh sách. Vì hte ArrayList chứa đầy các Đối tượng, bạn phải truyền chúng trước khi có thể gọi thanh. Nhưng vì Danh sách chung của Foo chỉ có thể chứa Foos, bạn có thể gọi trực tiếp Bar () trên các Foo đó.

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}

Tôi đoán bạn đã trả lời trước khi đọc câu hỏi. Đó là hai trường hợp không thực sự giúp tôi nhiều (được mô tả trong câu hỏi). Có những trường hợp khác mà nó làm điều gì đó hữu ích?
Bill K

4

Bạn chưa bao giờ viết một phương thức (hoặc một lớp) trong đó khái niệm chính của phương thức / lớp không bị ràng buộc chặt chẽ với một kiểu dữ liệu cụ thể của các tham số / biến phiên bản (hãy nghĩ đến danh sách liên kết, hàm max / min, tìm kiếm nhị phân , Vân vân.).

Bạn chưa bao giờ ước mình có thể sử dụng lại thuật toán / mã mà không cần sử dụng lại cắt-n-dán hoặc ảnh hưởng đến việc nhập mạnh (ví dụ: tôi muốn một Listchuỗi, không phải một Listtrong những thứ tôi hy vọng là chuỗi!)?

Đó là lý do tại sao bạn nên muốn sử dụng thuốc generic (hoặc một cái gì đó tốt hơn).


Tôi chưa bao giờ phải sao chép / dán tái sử dụng cho loại nội dung này (Không chắc điều đó sẽ xảy ra như thế nào?) Bạn triển khai một giao diện phù hợp với nhu cầu của mình và sử dụng giao diện đó. Trường hợp hiếm hoi mà bạn không thể làm điều này là khi bạn đang viết một tiện ích thuần túy (chẳng hạn như các bộ sưu tập) và đối với những tiện ích đó (như tôi đã mô tả trong câu hỏi) thì nó thực sự không bao giờ là một vấn đề. Tôi thỏa hiệp với việc gõ mạnh - Giống như bạn phải làm nếu bạn sử dụng phản xạ. Bạn có hoàn toàn từ chối sử dụng phản xạ vì bạn không thể gõ mạnh? (Nếu tôi phải sử dụng phản chiếu, tôi chỉ gói gọn nó như một tập hợp).
Bill K

3

Đừng quên rằng generic không chỉ được sử dụng bởi các lớp, chúng cũng có thể được sử dụng bởi các phương thức. Ví dụ: lấy đoạn mã sau:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

Nó đơn giản, nhưng có thể được sử dụng rất trang nhã. Điều thú vị là phương thức này trả về bất cứ thứ gì mà nó đã được đưa ra. Điều này giúp ích khi bạn đang xử lý các ngoại lệ cần được trả lại cho người gọi:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

Vấn đề là bạn không bị mất kiểu bằng cách chuyển nó qua một phương thức. Bạn có thể ném đúng loại ngoại lệ thay vì chỉThrowable , đó sẽ là tất cả những gì bạn có thể làm mà không có generic.

Đây chỉ là một ví dụ đơn giản về một lần sử dụng cho các phương pháp chung. Có một số điều khác bạn có thể làm với các phương pháp chung chung. Điều thú vị nhất, theo ý kiến ​​của tôi, là kiểu suy luận bằng generic. Lấy ví dụ sau (lấy từ Phiên bản Java thứ 2 hiệu quả của Josh Bloch):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Điều này không làm được nhiều, nhưng nó cắt giảm một số lộn xộn khi các kiểu chung dài (hoặc lồng nhau; tức là Map<String, List<String>>).


Tôi không nghĩ điều đó đúng về các trường hợp ngoại lệ. Nó khiến tôi phải suy nghĩ, nhưng tôi chắc chắn 95% là bạn sẽ nhận được kết quả chính xác như nhau mà không cần sử dụng chung chung (và mã rõ ràng hơn). Thông tin loại là một phần của đối tượng, không phải thứ bạn truyền nó tới. Tuy nhiên, tôi muốn thử nó - có lẽ ngày mai tôi sẽ đi làm và quay lại với bạn .. Nói cho bạn biết, tôi sẽ thử và nếu bạn đúng, tôi sẽ vượt qua danh sách bài viết của bạn và bình chọn cho 5 bài đăng của bạn :) Nếu không, bạn có thể muốn xem xét rằng sự sẵn có đơn giản của generic đã khiến bạn phức tạp hóa mã của mình không có lợi.
Bill K

Đúng là điều này làm tăng thêm sự phức tạp. Tuy nhiên, tôi nghĩ rằng nó có một số công dụng. Vấn đề là bạn không thể ném một cái cũ rõ ràng Throwabletừ bên trong thân phương thức có các ngoại lệ được khai báo cụ thể. Cách thay thế là viết các phương thức riêng biệt để trả về từng loại ngoại lệ hoặc tự thực hiện ép kiểu với một phương thức không chung chung trả về a Throwable. Cái trước quá dài dòng và khá vô dụng và cái sau sẽ không nhận được bất kỳ sự trợ giúp nào từ trình biên dịch. Bằng cách sử dụng generic, trình biên dịch sẽ tự động chèn đúng kiểu cho bạn. Vì vậy, câu hỏi đặt ra là: điều đó có xứng đáng với sự phức tạp không?
jigawot

Ồ, tôi hiểu rồi. Vì vậy, nó tránh được việc dàn diễn viên ra ngoài ... điểm tốt. Tôi nợ bạn 5.
Bill K

2

Ưu điểm chính, như Mitchel chỉ ra, là gõ mạnh mà không cần xác định nhiều lớp.

Bằng cách này, bạn có thể làm những việc như:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

Nếu không có generic, bạn sẽ phải truyền blah [0] đến đúng loại để truy cập các chức năng của nó.


Được lựa chọn, tôi thà không cast còn hơn cast.
MrBoJangle 16-08

Gõ mạnh dễ dàng là khía cạnh tốt nhất của imho generics, đặc biệt là khi kiểm tra kiểu thời gian biên dịch cho phép.
ljs 16/09/08

2

kiểu jvm nào cũng vậy ... nó hoàn toàn tạo ra mã coi kiểu chung là "Đối tượng" và tạo ra các phôi theo kiểu khởi tạo mong muốn. Các generic Java chỉ là đường cú pháp.


2

Tôi biết đây là một câu hỏi C #, nhưng chung chung các ngôn ngữ cũng được sử dụng trong các ngôn ngữ khác và việc sử dụng / mục tiêu của chúng khá giống nhau.

Bộ sưu tập Java sử dụng generic kể từ Java 1.5. Vì vậy, một nơi tốt để sử dụng chúng là khi bạn đang tạo đối tượng giống bộ sưu tập của riêng mình.

Một ví dụ mà tôi thấy hầu như ở khắp mọi nơi là lớp Pair, chứa hai đối tượng, nhưng cần phải xử lý các đối tượng đó theo cách chung.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

Bất cứ khi nào bạn sử dụng lớp Pair này, bạn có thể chỉ định loại đối tượng nào bạn muốn nó xử lý và mọi vấn đề về kiểu ép kiểu sẽ hiển thị tại thời điểm biên dịch, thay vì thời gian chạy.

Generics cũng có thể có giới hạn của chúng được xác định với các từ khóa 'siêu' và 'mở rộng'. Ví dụ: nếu bạn muốn xử lý một kiểu chung nhưng bạn muốn đảm bảo rằng nó mở rộng một lớp có tên Foo (có phương thức setTitle):

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

Mặc dù tự nó không thú vị lắm, nhưng thật hữu ích khi biết rằng bất cứ khi nào bạn xử lý FooManager, bạn biết rằng nó sẽ xử lý các loại MyClass và MyClass mở rộng Foo.


2

Từ tài liệu Sun Java, để trả lời "tại sao tôi nên sử dụng generic?":

"Generics cung cấp một cách để bạn giao tiếp loại bộ sưu tập với trình biên dịch để có thể kiểm tra nó. Sau khi trình biên dịch biết loại phần tử của bộ sưu tập, trình biên dịch có thể kiểm tra xem bạn đã sử dụng bộ sưu tập một cách nhất quán chưa và có thể chèn nhập đúng các giá trị được lấy ra khỏi bộ sưu tập ... Mã sử ​​dụng generic rõ ràng và an toàn hơn .... trình biên dịch có thể xác minh tại thời điểm biên dịch rằng các ràng buộc kiểu không bị vi phạm tại thời điểm chạy [nhấn mạnh của tôi]. Bởi vì chương trình biên dịch mà không có cảnh báo, chúng tôi có thể khẳng định chắc chắn rằng nó sẽ không ném ClassCastException vào thời gian chạy. Hiệu quả thực sự của việc sử dụng generic, đặc biệt là trong các chương trình lớn, là cải thiện khả năng đọc và độ mạnh mẽ . [nhấn mạnh của tôi] "


Tôi chưa bao giờ tìm thấy trường hợp nào mà việc nhập các bộ sưu tập chung chung sẽ giúp ích cho mã của tôi. Bộ sưu tập của tôi có phạm vi chặt chẽ. Đó là lý do tại sao tôi nói câu này "Nó có thể giúp tôi không?". Tuy nhiên, là một câu hỏi phụ, bạn có nói rằng trước khi bắt đầu sử dụng thuốc chung, điều này đôi khi xảy ra với bạn không? (đặt sai loại đối tượng trong bộ sưu tập của bạn). Đối với tôi, nó dường như là một giải pháp không có vấn đề gì thực sự, nhưng có lẽ tôi chỉ may mắn.
Bill K

@Bill K: nếu mã được kiểm soát chặt chẽ (nghĩa là chỉ được sử dụng bởi bạn) có lẽ bạn sẽ không bao giờ gặp phải vấn đề này. Thật tuyệt! Rủi ro lớn nhất là trong các dự án lớn có nhiều người cùng làm, IMO. Đó là một mạng lưới an toàn, và là một "phương pháp hay nhất".
Demi

@Bill K: Đây là một ví dụ. Giả sử bạn có một phương thức trả về ArrayList. Giả sử rằng ArrayList không phải là một tập hợp chung. Mã của ai đó lấy ArrayList này và cố gắng thực hiện một số thao tác trên các mục của nó. Vấn đề duy nhất là bạn đã điền BillTypes vào ArrayList và người tiêu dùng đang cố gắng vận hành trên ConsumerTypes. Điều này biên dịch, nhưng sẽ nổ tung trong thời gian chạy. Nếu bạn sử dụng các bộ sưu tập chung, bạn sẽ gặp lỗi trình biên dịch, điều này dễ xử lý hơn nhiều.
Demi

@Bill K: Tôi đã làm việc với những người có trình độ kỹ năng khác nhau. Tôi không có quyền lựa chọn người mà tôi chia sẻ dự án với. Vì vậy, an toàn kiểu loại là rất quan trọng đối với tôi. Có, tôi đã tìm thấy (và sửa chữa) lỗi bằng cách chuyển đổi mã để sử dụng Generics.
Eddie

1

Generics cho phép bạn tạo các đối tượng được gõ mạnh, nhưng bạn không phải xác định kiểu cụ thể. Tôi nghĩ ví dụ hữu ích nhất là Danh sách và các lớp tương tự.

Sử dụng danh sách chung, bạn có thể có một Danh sách Danh sách bất cứ thứ gì bạn muốn và bạn luôn có thể tham khảo cách gõ mạnh, bạn không cần phải chuyển đổi hoặc bất cứ điều gì giống như bạn làm với Mảng hoặc Danh sách chuẩn.


1

Generics cho phép bạn sử dụng cách gõ mạnh cho các đối tượng và cấu trúc dữ liệu có thể chứa bất kỳ đối tượng nào. Nó cũng giúp loại bỏ các kiểu gõ tẻ nhạt và tốn kém khi lấy các đối tượng từ các cấu trúc chung (quyền anh / mở hộp).

Một ví dụ sử dụng cả hai là danh sách liên kết. Một lớp danh sách liên kết sẽ có ích gì nếu nó chỉ có thể sử dụng đối tượng Foo? Để triển khai danh sách liên kết có thể xử lý bất kỳ loại đối tượng nào, danh sách được liên kết và các nút trong lớp bên trong nút giả định phải là chung nếu bạn muốn danh sách chỉ chứa một loại đối tượng.


1

Nếu bộ sưu tập của bạn chứa các loại giá trị, chúng không cần đóng hộp / mở hộp đối với các đối tượng khi được chèn vào bộ sưu tập để hiệu suất của bạn tăng đáng kể. Các tiện ích bổ sung thú vị như trình sạc lại có thể tạo nhiều mã hơn cho bạn, như vòng lặp foreach.


1

Một lợi thế khác của việc sử dụng Generics (đặc biệt với Bộ sưu tập / Danh sách) là bạn có thể kiểm tra loại thời gian biên dịch. Điều này thực sự hữu ích khi sử dụng Danh sách Chung thay vì Danh sách Đối tượng.


1

Lý do duy nhất là họ cung cấp an toàn cho Loại

List<Customer> custCollection = new List<Customer>;

như trái ngược với,

object[] custCollection = new object[] { cust1, cust2 };

như một ví dụ đơn giản.


Nhập an toàn, một cuộc thảo luận của chính nó. Có lẽ ai đó nên đặt một câu hỏi về nó.
MrBoJangle 17/09/08

Vâng, nhưng bạn đang ám chỉ điều gì? Toàn bộ điểm của An toàn kiểu loại?
Vin

1

Tóm lại, generic cho phép bạn chỉ định chính xác hơn những gì bạn định làm (gõ mạnh hơn).

Điều này có một số lợi ích cho bạn:

  • Bởi vì trình biên dịch biết nhiều hơn về những gì bạn muốn làm, nó cho phép bạn bỏ qua rất nhiều kiểu ép kiểu vì nó đã biết rằng kiểu sẽ tương thích.

  • Điều này cũng giúp bạn có phản hồi sớm hơn về những điểm chính xác của chương trình của bạn. Những thứ mà trước đây sẽ không thành công trong thời gian chạy (ví dụ: vì một đối tượng không thể được truyền theo kiểu mong muốn), bây giờ bị lỗi trong thời gian biên dịch và bạn có thể sửa lỗi trước khi bộ phận kiểm tra của bạn gửi báo cáo lỗi kỹ thuật số.

  • Trình biên dịch có thể thực hiện nhiều tối ưu hóa hơn, như tránh đấm bốc, v.v.


1

Một số điều cần thêm / mở rộng (nói theo quan điểm .NET):

Các kiểu chung cho phép bạn tạo các lớp và giao diện dựa trên vai trò. Điều này đã được nói trong các thuật ngữ cơ bản hơn, nhưng tôi thấy bạn bắt đầu thiết kế mã của mình với các lớp được triển khai theo kiểu bất khả tri - dẫn đến mã có thể tái sử dụng cao.

Các lập luận chung chung về các phương thức có thể làm điều tương tự, nhưng chúng cũng giúp áp dụng nguyên tắc "Tell Don't Ask" để ép kiểu, tức là "đưa cho tôi thứ tôi muốn, và nếu bạn không thể, bạn cho tôi biết tại sao".


1

Tôi sử dụng chúng chẳng hạn trong một GenericDao được triển khai với SpringORM và Hibernate trông như thế này

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

Bằng cách sử dụng generics, việc triển khai DAO này của tôi buộc nhà phát triển phải chuyển chúng chỉ những thực thể mà chúng được thiết kế bằng cách chỉ phân lớp GenericDao

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

Khung công tác nhỏ của tôi mạnh mẽ hơn (có những thứ như lọc, tải lười biếng, tìm kiếm). Tôi chỉ đơn giản hóa ở đây để cung cấp cho bạn một ví dụ

Tôi cũng như Steve và bạn, lúc đầu nói "Quá lộn xộn và phức tạp" nhưng bây giờ tôi thấy ưu điểm của nó


1

Các lợi ích rõ ràng như "an toàn kiểu" và "không truyền" đã được đề cập nên có lẽ tôi có thể nói về một số "lợi ích" khác mà tôi hy vọng nó sẽ hữu ích.

Trước hết, generics là một khái niệm không phụ thuộc vào ngôn ngữ và IMO, nó có thể có ý nghĩa hơn nếu bạn nghĩ về tính đa hình thông thường (thời gian chạy) cùng một lúc.

Ví dụ, tính đa hình như chúng ta biết từ thiết kế hướng đối tượng có khái niệm thời gian chạy trong đó đối tượng người gọi được tìm ra trong thời gian chạy khi thực thi chương trình và phương thức liên quan được gọi tương ứng tùy thuộc vào loại thời gian chạy. Trong generic, ý tưởng hơi giống nhau nhưng mọi thứ xảy ra tại thời điểm biên dịch. Điều đó có nghĩa là gì và bạn sử dụng nó như thế nào?

(Hãy gắn bó với các phương thức chung để giữ cho nó nhỏ gọn) Điều đó có nghĩa là bạn vẫn có thể có cùng một phương thức trên các lớp riêng biệt (giống như bạn đã làm trước đây trong các lớp đa hình) nhưng lần này chúng được trình biên dịch tự động tạo tùy thuộc vào các kiểu được thiết lập tại thời điểm biên dịch. Bạn tham số các phương thức của mình theo kiểu bạn cung cấp tại thời điểm biên dịch. Vì vậy, thay vì viết các phương pháp từ đầu cho mọi loại bạn có như khi bạn làm trong đa hình thời gian chạy (ghi đè phương thức), bạn để các trình biên dịch thực hiện công việc trong quá trình biên dịch. Điều này có một lợi thế rõ ràng vì bạn không cần phải suy ra tất cả các kiểu có thể được sử dụng trong hệ thống của bạn, điều này làm cho nó có thể mở rộng hơn nhiều mà không cần thay đổi mã.

Các lớp học hoạt động theo cùng một cách. Bạn tham số kiểu và mã được tạo bởi trình biên dịch.

Một khi bạn có ý tưởng về "thời gian biên dịch", bạn có thể sử dụng các kiểu "giới hạn" và hạn chế những gì có thể được truyền dưới dạng kiểu tham số thông qua các lớp / phương thức. Vì vậy, bạn có thể kiểm soát những gì sẽ được thông qua, đó là một điều mạnh mẽ, đặc biệt là bạn có một khuôn khổ đang bị người khác sử dụng.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

Không ai có thể đặt sth ngoài MyObject bây giờ.

Ngoài ra, bạn có thể "thực thi" các ràng buộc kiểu đối với các đối số phương thức của mình, nghĩa là bạn có thể đảm bảo rằng cả hai đối số phương thức của bạn sẽ phụ thuộc vào cùng một kiểu.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

Hy vọng tất cả những điều này có ý nghĩa.



0

Sử dụng generic cho các bộ sưu tập rất đơn giản và gọn gàng. Ngay cả khi bạn chơi nó ở mọi nơi khác, thu được từ các bộ sưu tập là một chiến thắng đối với tôi.

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vs

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

hoặc là

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

Chỉ điều đó thôi cũng đáng với "chi phí" biên của thuốc chung và bạn không cần phải là một Guru chung chung để sử dụng cái này và nhận được giá trị.


1
Nếu không có generic, bạn có thể làm như sau: List thingsList = getStuff (); for (Đối tượng thứ :uffList) {((Nội dung) thứ) .doStuff (); }, không khác nhiều.
Tom Hawtin - tackline

Nếu không có generic, tôi thực hiện nhồi nhét tin nhắn.doAll (); Tôi đã nói rằng tôi luôn có một lớp wrapper chính xác là nơi dành cho mã như vậy. Nếu không, làm thế nào để bạn tránh sao chép vòng lặp này ở nơi khác? Bạn đặt nó ở đâu để tái sử dụng? Lớp wrapper có lẽ sẽ có một vòng lặp theo kiểu nào đó, nhưng trong lớp đó, vì lớp đó là về "Thứ", việc sử dụng một vòng lặp for được ép kiểu như Tom đã đề xuất ở trên là khá rõ ràng / tự ghi lại.
Bill K

@Jherico, Điều đó thực sự thú vị. Tôi tự hỏi liệu có lý do gì khiến bạn cảm thấy như vậy không, và nếu đó là điều tôi không hiểu - rằng có điều gì đó trong bản chất các dân tộc tìm thấy bất kỳ lý do nào khiến mọi người trở nên tục tĩu và sẵn sàng đi đến những độ dài không tự nhiên (như thêm một cú pháp phức tạp hơn nhiều) để tránh nó. Tại sao "Nếu bạn phải cast một cái gì đó, bạn đã mất"?
Bill K

@Jherico: theo tài liệu của Sun, generics cung cấp cho trình biên dịch kiến ​​thức về những gì cần truyền đối tượng. Vì trình biên dịch thực hiện truyền cho generic, chứ không phải cho người dùng, điều này có thay đổi tuyên bố của bạn không?
Demi

Tôi đã chuyển đổi mã từ trước Java 1.5 sang Generics và trong quá trình này, tôi đã tìm thấy lỗi khi loại đối tượng sai được đưa vào bộ sưu tập. Tôi cũng rơi vào tình trạng "nếu bạn phải truyền bạn đã thua", bởi vì nếu bạn phải truyền theo cách thủ công, bạn có nguy cơ gặp phải ClassCastException. Với Generics trình biên dịch làm nó vô hình cho bạn, nhưng cơ hội của một ClassCastException là cách giảm do sự an toàn loại. Vâng, không có Generics, bạn có thể sử dụng Object, nhưng đó là một mùi mã khủng khiếp đối với tôi, giống như "ném Ngoại lệ".
Eddie

0

Generics cũng cung cấp cho bạn khả năng tạo nhiều đối tượng / phương thức có thể tái sử dụng hơn trong khi vẫn cung cấp hỗ trợ loại cụ thể. Bạn cũng đạt được rất nhiều hiệu suất trong một số trường hợp. Tôi không biết thông số kỹ thuật đầy đủ về Java Generics, nhưng trong .NET, tôi có thể chỉ định các ràng buộc trên tham số Kiểu, như Triển khai giao diện, Khối mã lệnh và Khởi tạo.


0
  1. Cho phép lập trình viên triển khai các thuật toán chung - Bằng cách sử dụng các thuật toán chung, các lập trình viên có thể triển khai các thuật toán chung hoạt động trên các tập hợp thuộc các kiểu khác nhau, có thể được tùy chỉnh, an toàn về kiểu và dễ đọc hơn.

  2. Kiểm tra kiểu mạnh hơn tại thời điểm biên dịch - Trình biên dịch Java áp dụng kiểm tra kiểu mạnh cho mã chung và đưa ra lỗi nếu mã vi phạm an toàn kiểu. Sửa lỗi thời gian biên dịch dễ hơn sửa lỗi thời gian chạy, có thể khó tìm.

  3. Loại bỏ phôi.

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.