"Chào, Amigo!"

"Chào, Ellie!"

"Hôm nay, Rishi và tôi sẽ kể cho các bạn nghe về thuốc generic."

"Đợi đã, tôi nghĩ tôi đã biết hầu hết mọi thứ rồi."

"Hầu hết mọi thứ, nhưng không phải tất cả."

"Thật sao? OK, tôi sẵn sàng nghe đây."

"Vậy chúng ta bắt đầu đi."

"Trong Java, generic là các lớp có tham số kiểu."

"Về lý do tại sao thuốc generic được phát minh, hãy xem các nhận xét trong mã:"

Ví dụ
ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

Cách giải quyết vấn đề bằng Generics:

Ví dụ
ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

"Đơn giản là mã này sẽ không được biên dịch và lỗi do thêm loại dữ liệu sai sẽ được chú ý trong quá trình biên dịch."

"Vâng, tôi đã biết điều này."

"Được rồi, tốt. Sự lặp lại là mẹ của việc học."

"Nhưng những người tạo ra Java đã hơi lười biếng khi họ tạo ra các generic. Thay vì tạo ra các kiểu đầy đủ với các tham số, họ lại đưa vào một sự tối ưu hóa khéo léo. Trên thực tế, họ đã không thêm bất kỳ thông tin nào về các tham số kiểu vào generics. Thay vào đó, tất cả các ma thuật xảy ra trong quá trình biên dịch."

Mã với thuốc generic
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
Điều gì thực sự xảy ra
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

"Đó là trơn."

"Có, nhưng cách tiếp cận này có một tác dụng phụ.  Không có thông tin nào về các tham số kiểu được lưu trữ bên trong một lớp chung.  Cách tiếp cận này sau này được gọi là xóa kiểu ."

"Nói cách khác, nếu bạn có lớp của riêng mình với các tham số kiểu, thì bạn không thể sử dụng thông tin về chúng bên trong lớp."

Mã với thuốc generic
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
Điều gì thực sự xảy ra
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

"Trong quá trình biên dịch, tất cả các loại tham số được thay thế bằng Đối tượng. Và bên trong lớp không có thông tin về loại được truyền cho nó."

"Vâng, tôi đồng ý, đó không phải là tốt nhất."

"Nó không đáng sợ đến thế đâu. Tôi sẽ nói cho bạn biết cách giải quyết vấn đề này sau."

Nhưng có nhiều hơn nữa. Java cho phép bạn chỉ định kiểu cha cho các tham số kiểu. Từ khóa mở rộng được sử dụng cho việc này. Ví dụ:

Mã với thuốc generic
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
Điều gì thực sự xảy ra
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

"Lưu ý hai sự thật:"

"Đầu tiên, bạn không thể chuyển bất kỳ loại nào làm tham số — bạn chỉ có thể chuyển một Cat hoặc một lớp kế thừa Cat."

"Thứ hai, bên trong lớp Zoo, các biến kiểu T giờ đây có thể gọi các phương thức của lớp Cat.  Cột bên phải giải thích lý do tại sao (vì Cat sẽ được thay thế ở mọi nơi có chữ T)"

"Vâng. Nếu chúng ta nói rằng Cat hoặc một lớp con của Cat được chuyển thành đối số kiểu, thì chúng ta chắc chắn rằng kiểu T sẽ luôn có các phương thức của lớp Cat."

"Chà, thật là thông minh."