Java Generics - Hướng dẫn nhanh

Sẽ thật tuyệt nếu chúng ta có thể viết một phương thức sắp xếp duy nhất có thể sắp xếp các phần tử trong mảng Số nguyên, mảng Chuỗi hoặc một mảng thuộc bất kỳ kiểu nào hỗ trợ sắp xếp.

Các phương thức chung của Java và các lớp chung cho phép người lập trình chỉ định, với một khai báo phương thức duy nhất, một tập hợp các phương thức liên quan hoặc với một khai báo lớp đơn, một tập hợp các kiểu liên quan, tương ứng.

Generics cũng cung cấp sự an toàn cho kiểu thời gian biên dịch cho phép người lập trình bắt các kiểu không hợp lệ tại thời điểm biên dịch.

Sử dụng khái niệm Java Generic, chúng ta có thể viết một phương thức chung để sắp xếp một mảng đối tượng, sau đó gọi phương thức chung với mảng Số nguyên, Mảng đôi, Mảng chuỗi, v.v. để sắp xếp các phần tử của mảng.

Thiết lập môi trường cục bộ

JUnit là một khuôn khổ cho Java, vì vậy yêu cầu đầu tiên là phải cài đặt JDK trong máy của bạn.

Yêu cầu hệ thống

JDK 1,5 trở lên.
Ký ức Không có yêu cầu tối thiểu.
Dung lượng đĩa Không có yêu cầu tối thiểu.
Hệ điều hành Không có yêu cầu tối thiểu.

Bước 1: Xác minh cài đặt Java trong máy của bạn

Trước hết, hãy mở bảng điều khiển và thực hiện một lệnh java dựa trên hệ điều hành bạn đang làm việc.

Hệ điều hành Bài tập Chỉ huy
các cửa sổ Mở bảng điều khiển lệnh c: \> java -version
Linux Mở Command Terminal $ java -version
Mac Mở thiết bị đầu cuối máy: <joseph $ java -version

Hãy xác minh kết quả đầu ra cho tất cả các hệ điều hành -

Hệ điều hành Đầu ra
các cửa sổ

phiên bản java "1.6.0_21"

Môi trường thời gian chạy Java (TM) SE (bản dựng 1.6.0_21-b07)

Máy khách Java HotSpot (TM) (xây dựng 17.0-b17, chế độ hỗn hợp, chia sẻ)

Linux

phiên bản java "1.6.0_21"

Môi trường thời gian chạy Java (TM) SE (bản dựng 1.6.0_21-b07)

Máy khách Java HotSpot (TM) (xây dựng 17.0-b17, chế độ hỗn hợp, chia sẻ)

Mac

phiên bản java "1.6.0_21"

Môi trường thời gian chạy Java (TM) SE (bản dựng 1.6.0_21-b07)

Máy chủ ảo Java HotSpot (TM) 64-Bit (bản dựng 17.0-b17, chế độ hỗn hợp, chia sẻ)

Nếu bạn chưa cài đặt Java trên hệ thống của mình, hãy tải xuống Bộ phát triển phần mềm Java (SDK) từ liên kết sau https://www.oracle.com. Chúng tôi giả định Java 1.6.0_21 là phiên bản đã cài đặt cho hướng dẫn này.

Bước 2: Đặt Môi trường JAVA

Đặt JAVA_HOMEbiến môi trường để trỏ đến vị trí thư mục cơ sở nơi Java được cài đặt trên máy của bạn. Ví dụ.

Hệ điều hành Đầu ra
các cửa sổ Đặt biến môi trường JAVA_HOME thành C: \ Program Files \ Java \ jdk1.6.0_21
Linux xuất JAVA_HOME = / usr / local / java-current
Mac export JAVA_HOME = / Library / Java / Home

Nối vị trí trình biên dịch Java vào Đường dẫn Hệ thống.

Hệ điều hành Đầu ra
các cửa sổ Nối chuỗi C:\Program Files\Java\jdk1.6.0_21\bin ở cuối biến hệ thống, Path.
Linux xuất PATH = $PATH:$JAVA_HOME / bin /
Mac không yêu cầu

Xác minh cài đặt Java bằng lệnh java -version Như đã giải thích ở trên.

Một khai báo lớp chung trông giống như một khai báo lớp không chung chung, ngoại trừ việc tên lớp được theo sau bởi một phần tham số kiểu.

Phần tham số kiểu của một lớp chung có thể có một hoặc nhiều tham số kiểu được phân tách bằng dấu phẩy. Các lớp này được gọi là lớp được tham số hóa hoặc kiểu được tham số hóa vì chúng chấp nhận một hoặc nhiều tham số.

Cú pháp

public class Box<T> {
   private T t;
}

Ở đâu

  • Box - Hộp là một lớp chung.

  • T- Tham số kiểu chung được truyền cho lớp chung. Nó có thể lấy bất kỳ Đối tượng nào.

  • t - Bản sao của loại T chung chung.

Sự miêu tả

T là một tham số kiểu được truyền cho Box lớp chung và phải được truyền khi một đối tượng Box được tạo.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
String Value :Hello World

Theo quy ước, các tên tham số kiểu được đặt tên là các chữ cái viết hoa đơn lẻ để một tham số kiểu có thể được phân biệt dễ dàng với một lớp hoặc tên giao diện thông thường. Sau đây là danh sách các tên tham số kiểu thường được sử dụng:

  • E - Phần tử, và được sử dụng chủ yếu bởi khung công tác Java Collections.

  • K - Khóa, và chủ yếu được sử dụng để thể hiện loại tham số của khóa của bản đồ.

  • V - Giá trị, và chủ yếu được sử dụng để biểu thị loại tham số của giá trị của bản đồ.

  • N - Số, và chủ yếu được sử dụng để biểu diễn số.

  • T - Kiểu, và chủ yếu được sử dụng để biểu diễn tham số kiểu chung đầu tiên.

  • S - Kiểu, và chủ yếu được sử dụng để biểu diễn tham số kiểu chung thứ hai.

  • U - Kiểu, và chủ yếu được sử dụng để biểu diễn tham số kiểu chung thứ ba.

  • V - Kiểu, và chủ yếu được sử dụng để biểu diễn tham số kiểu chung thứ tư.

Ví dụ sau sẽ giới thiệu khái niệm được đề cập ở trên.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Pair<String, Integer> pair = new Pair<String, Integer>(); 
      pair.addKeyValue("1", Integer.valueOf(10));
      System.out.printf("(Pair)Integer Value :%d\n", pair.getValue("1"));

      CustomList<Box> list = new CustomList<Box>();
      list.addItem(box);
      System.out.printf("(CustomList)Integer Value :%d\n", list.getItem(0).getFirst());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

class Pair<K,V>{
   private Map<K,V> map = new HashMap<K,V>();

   public void addKeyValue(K key, V value) {
      map.put(key, value);
   }

   public V getValue(K key) {
      return map.get(key);
   }
}

class CustomList<E>{
   private List<E> list = new ArrayList<E>();

   public void addItem(E value) {
      list.add(value);
   }

   public E getItem(int index) {
      return list.get(index);
   }
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
String Value :Hello World
(Pair)Integer Value :10
(CustomList)Integer Value :10

Suy luận kiểu thể hiện khả năng của trình biên dịch Java xem xét một lệnh gọi phương thức và khai báo tương ứng của nó để kiểm tra và xác định (các) đối số kiểu. Thuật toán suy luận kiểm tra kiểu của các đối số và nếu có, kiểu được gán sẽ được trả về. Các thuật toán suy luận cố gắng tìm một kiểu cụ thể có thể lấp đầy tất cả các tham số kiểu.

Trình biên dịch tạo ra cảnh báo chuyển đổi không được kiểm tra trong trường hợp loại suy luận không được sử dụng.

Cú pháp

Box<Integer> integerBox = new Box<>();

Ở đâu

  • Box - Hộp là một lớp chung.

  • <> - Toán tử kim cương biểu thị kiểu suy luận.

Sự miêu tả

Sử dụng toán tử kim cương, trình biên dịch xác định kiểu của tham số. Toán tử này có sẵn từ phiên bản Java SE 7 trở đi.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      //type inference   
      Box<Integer> integerBox = new Box<>();
      //unchecked conversion warning
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
String Value :Hello World

Bạn có thể viết một khai báo phương thức chung duy nhất có thể được gọi với các đối số thuộc các kiểu khác nhau. Dựa trên các loại đối số được truyền cho phương thức chung, trình biên dịch xử lý từng lệnh gọi phương thức một cách thích hợp. Sau đây là các quy tắc để xác định các Phương thức Chung -

  • Tất cả các khai báo phương thức chung đều có phần tham số kiểu được phân cách bằng dấu ngoặc nhọn (<và>) đứng trước kiểu trả về của phương thức (<E> trong ví dụ tiếp theo).

  • Mỗi phần tham số kiểu chứa một hoặc nhiều tham số kiểu được phân tách bằng dấu phẩy. Tham số kiểu, còn được gọi là biến kiểu, là một số nhận dạng chỉ định tên kiểu chung.

  • Các tham số kiểu có thể được sử dụng để khai báo kiểu trả về và hoạt động như trình giữ chỗ cho các kiểu đối số được truyền cho phương thức chung, được gọi là đối số kiểu thực.

  • Phần thân của một phương thức chung được khai báo giống như của bất kỳ phương thức nào khác. Lưu ý rằng các tham số kiểu chỉ có thể đại diện cho các kiểu tham chiếu, không phải kiểu nguyên thủy (như int, double và char).

Thí dụ

Ví dụ sau minh họa cách chúng ta có thể in một mảng có kiểu khác nhau bằng một phương thức Chung duy nhất:

public class GenericMethodTest {
   // generic method printArray
   public static < E > void printArray( E[] inputArray ) {
      // Display array elements
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
      // Create arrays of Integer, Double and Character
      Integer[] intArray = { 1, 2, 3, 4, 5 };
      Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
      Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

      System.out.println("Array integerArray contains:");
      printArray(intArray);   // pass an Integer array

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);   // pass a Double array

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);   // pass a Character array
   }
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Array integerArray contains:
1 2 3 4 5 

Array doubleArray contains:
1.1 2.2 3.3 4.4 

Array characterArray contains:
H E L L O

Một lớp Chung có thể có các tham số kiểu nhiều đối tượng. Ví dụ sau sẽ giới thiệu khái niệm được đề cập ở trên.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Box<String, String> box1 = new Box<String, String>();
      box1.add("Message","Hello World");
      System.out.printf("String Value :%s\n", box1.getFirst());
      System.out.printf("String Value :%s\n", box1.getSecond());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
String Value :Hello World
String Value :Message
String Value :Hello World

Một lớp Generic có thể có các kiểu được tham số hóa trong đó một tham số kiểu có thể được thay thế bằng một kiểu được tham số hóa. Ví dụ sau sẽ giới thiệu khái niệm được đề cập ở trên.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;


public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, List<String>> box
         = new Box<Integer, List<String>>();
      
      List<String> messages = new ArrayList<String>();
      
      messages.add("Hi");
      messages.add("Hello");
      messages.add("Bye");      
      
      box.add(Integer.valueOf(10),messages);
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
String Value :[Hi, Hello, Bye]

Kiểu thô là một đối tượng của một lớp hoặc giao diện chung nếu các đối số kiểu của nó không được truyền trong quá trình tạo. Ví dụ sau sẽ giới thiệu khái niệm được đề cập ở trên.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> box = new Box<Integer>();
      
      box.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", box.getData());
      
      
      Box rawBox = new Box();
      
      //No warning
      rawBox = box;
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //Warning for unchecked invocation to set(T)
      rawBox.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //Warning for unchecked conversion
      box = rawBox;
      System.out.printf("Integer Value :%d\n", box.getData());
   }
}

class Box<T> {
   private T t; 

   public void set(T t) {
      this.t = t;
   }

   public T getData() {
      return t;
   } 
}

Điều này sẽ tạo ra kết quả sau.

Đầu ra

Integer Value :10
Integer Value :10
Integer Value :10
Integer Value :10

Có thể có những lúc bạn muốn hạn chế các loại kiểu được phép chuyển cho tham số kiểu. Ví dụ, một phương thức hoạt động trên các số có thể chỉ muốn chấp nhận các thể hiện của Số hoặc các lớp con của nó. Đây là những gì các tham số kiểu bị giới hạn dành cho.

Để khai báo một tham số kiểu có giới hạn, hãy liệt kê tên của tham số kiểu, theo sau là từ khóa expand, tiếp theo là giới hạn trên của nó.

Thí dụ

Ví dụ sau minh họa cách dùng để mở rộng theo nghĩa chung là "mở rộng" (như trong các lớp) hoặc "thực hiện" (như trong các giao diện). Ví dụ này là phương thức Chung để trả về giá trị lớn nhất trong ba đối tượng có thể so sánh được -

public class MaximumTest {
   // determines the largest of three Comparable objects
   
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;   // assume x is initially the largest
      
      if(y.compareTo(max) > 0) {
         max = y;   // y is the largest so far
      }
      
      if(z.compareTo(max) > 0) {
         max = z;   // z is the largest now                 
      }
      return max;   // returns the largest object   
   }
   
   public static void main(String args[]) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));

      System.out.printf("Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum("pear", "apple", "orange"));
   }
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Max of pear, apple and orange is pear

Một tham số kiểu có thể có nhiều giới hạn.

Cú pháp

public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z)

Ở đâu

  • maximum - tối đa là một phương pháp chung.

  • T- Tham số kiểu chung được truyền cho phương thức chung. Nó có thể lấy bất kỳ Đối tượng nào.

Sự miêu tả

T là một tham số kiểu được truyền cho Box lớp chung và phải là kiểu con của lớp Số và phải tích hợp giao diện có thể so sánh được. Trong trường hợp một lớp được truyền dưới dạng ràng buộc, nó nên được chuyển trước trước giao diện, nếu không sẽ xảy ra lỗi thời gian biên dịch.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
   }

   public static <T extends Number 
      & Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;      
   }

   // Compiler throws error in case of below declaration
   /* public static <T extends Comparable<T> 
      & Number> T maximum1(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;   
   }*/
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Java đã cung cấp hỗ trợ chung trong giao diện Danh sách.

Cú pháp

List<T> list = new ArrayList<T>();

Ở đâu

  • list - đối tượng của giao diện Danh sách.

  • T - Tham số kiểu chung được truyền trong quá trình khai báo danh sách.

Sự miêu tả

T là một tham số kiểu được truyền vào Danh sách giao diện chung và lớp ArrayList của nó.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericsTester {
   public static void main(String[] args) {

      List<Integer> integerList = new ArrayList<Integer>();
  
      integerList.add(Integer.valueOf(10));
      integerList.add(Integer.valueOf(11));

      List<String> stringList = new ArrayList<String>();
  
      stringList.add("Hello World");
      stringList.add("Hi World");
 

      System.out.printf("Integer Value :%d\n", integerList.get(0));
      System.out.printf("String Value :%s\n", stringList.get(0));

      for(Integer data: integerList) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringList.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Integer Value :10
String Value :Hello World
Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Java đã cung cấp hỗ trợ chung trong giao diện Set.

Cú pháp

Set<T> set = new HashSet<T>();

Ở đâu

  • set - đối tượng của Set Interface.

  • T - Tham số kiểu chung được truyền trong khi khai báo tập hợp.

Sự miêu tả

T là một tham số kiểu được truyền cho giao diện chung Set và HashSet lớp áp dụng của nó.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

package com.tutorialspoint;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class GenericsTester {
   public static void main(String[] args) {

      Set<Integer> integerSet = new HashSet<Integer>();
  
      integerSet.add(Integer.valueOf(10));
      integerSet.add(Integer.valueOf(11));

      Set<String> stringSet = new HashSet<String>();
  
      stringSet.add("Hello World");
      stringSet.add("Hi World");
 

      for(Integer data: integerSet) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringSet.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Java đã cung cấp hỗ trợ chung trong giao diện Bản đồ.

Cú pháp

Set<T> set = new HashSet<T>();

Ở đâu

  • set - đối tượng của Set Interface.

  • T - Tham số kiểu chung được truyền trong khi khai báo tập hợp.

Sự miêu tả

T là một tham số kiểu được truyền cho giao diện chung Set và HashSet lớp áp dụng của nó.

Thí dụ

Tạo chương trình java sau bằng bất kỳ trình soạn thảo nào bạn chọn.

package com.tutorialspoint;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {

      Map<Integer,Integer> integerMap 
         = new HashMap<Integer,Integer>();
  
      integerMap.put(1, 10);
      integerMap.put(2, 11);

      Map<String,String> stringMap = new HashMap<String,String>();
    
      stringMap.put("1", "Hello World");
      stringMap.put("2","Hi World");
 

      System.out.printf("Integer Value :%d\n", integerMap.get(1));
      System.out.printf("String Value :%s\n", stringMap.get("1"));

      // iterate keys.
      Iterator<Integer> integerIterator   = integerMap.keySet().iterator();

      while(integerIterator.hasNext()) {
         System.out.printf("Integer Value :%d\n", integerIterator.next());
      }

      // iterate values.
      Iterator<String> stringIterator   = stringMap.values().iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Integer Value :10
String Value :Hello World
Integer Value :1
Integer Value :2
String Value :Hello World
String Value :Hi World

Dấu chấm hỏi (?), Đại diện cho ký tự đại diện, là viết tắt của loại không xác định trong generic. Có thể có những lúc bạn muốn hạn chế các loại kiểu được phép chuyển cho tham số kiểu. Ví dụ, một phương thức hoạt động trên các số có thể chỉ muốn chấp nhận các thể hiện của Số hoặc các lớp con của nó.

Để khai báo tham số Ký tự đại diện giới hạn trên, hãy liệt kê dấu?, Theo sau là từ khóa expand, tiếp theo là giới hạn trên của nó.

Thí dụ

Ví dụ sau minh họa cách dùng extension để chỉ định ký tự đại diện giới hạn trên.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {

   public static double sum(List<? extends Number> numberlist) {
      double sum = 0.0;
      for (Number n : numberlist) sum += n.doubleValue();
      return sum;
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      System.out.println("sum = " + sum(integerList));

      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      System.out.println("sum = " + sum(doubleList));
   }
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

sum = 6.0
sum = 7.0

Dấu chấm hỏi (?), Đại diện cho ký tự đại diện, là viết tắt của loại không xác định trong generic. Có thể có những lúc bất kỳ đối tượng nào có thể được sử dụng khi một phương thức có thể được triển khai bằng chức năng được cung cấp trong lớp Đối tượng hoặc Khi mã độc lập với tham số kiểu.

Để khai báo một tham số Ký tự đại diện không bị ràng buộc, hãy liệt kê dấu? chỉ có.

Thí dụ

Ví dụ sau minh họa cách dùng extension để chỉ định một ký tự đại diện không bị ràng buộc.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      printAll(integerList);
      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      printAll(doubleList);
   }
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

1 
2 
3 
1.2 
2.3 
3.5

Dấu chấm hỏi (?), Đại diện cho ký tự đại diện, là viết tắt của loại không xác định trong generic. Có thể có những lúc bạn muốn hạn chế các loại kiểu được phép chuyển cho tham số kiểu. Ví dụ: một phương thức hoạt động trên số có thể chỉ muốn chấp nhận các bản sao của Số nguyên hoặc các lớp cha của nó như Số.

Để khai báo một tham số Ký tự đại diện giới hạn dưới, hãy liệt kê dấu?, Theo sau là từ khóa super, tiếp theo là giới hạn dưới của nó.

Thí dụ

Ví dụ sau minh họa cách super được sử dụng để chỉ định một ký tự đại diện giới hạn dưới.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   public static void addCat(List<? super Cat> catList) {
      catList.add(new RedCat());
      System.out.println("Cat Added");
   }

   public static void main(String[] args) {
      List<Animal> animalList= new ArrayList<Animal>();
      List<Cat> catList= new ArrayList<Cat>();
      List<RedCat> redCatList= new ArrayList<RedCat>();
      List<Dog> dogList= new ArrayList<Dog>();

      //add list of super class Animal of Cat class
      addCat(animalList);

      //add list of Cat class
      addCat(catList);

      //compile time error
      //can not add list of subclass RedCat of Cat class
      //addCat(redCatList);

      //compile time error
      //can not add list of subclass Dog of Superclass Animal of Cat class
      //addCat.addMethod(dogList); 
   }
}
class Animal {}

class Cat extends Animal {}

class RedCat extends Cat {}

class Dog extends Animal {}

Điều này sẽ tạo ra kết quả sau:

Cat Added
Cat Added

Các ký tự đại diện có thể được sử dụng theo ba cách:

  • Upper bound Wildcard-? mở rộng Loại.

  • Lower bound Wildcard-? siêu Loại.

  • Unbounded Wildcard -?

Để quyết định loại ký tự đại diện nào phù hợp nhất với điều kiện, trước tiên chúng ta hãy phân loại loại tham số được truyền cho một phương thức là inout tham số.

  • in variable- Một biến trong cung cấp dữ liệu cho mã. Ví dụ, sao chép (src, dest). Ở đây src hoạt động như trong biến là dữ liệu được sao chép.

  • out variable- Một biến out giữ dữ liệu được cập nhật bởi mã. Ví dụ, sao chép (src, dest). Ở đây đích hoạt động như trong biến có dữ liệu được sao chép.

Hướng dẫn về Ký tự đại diện.

  • Upper bound wildcard - Nếu một biến là in danh mục, sử dụng từ khóa mở rộng với ký tự đại diện.

  • Lower bound wildcard - Nếu một biến là out danh mục, sử dụng từ khóa siêu với ký tự đại diện.

  • Unbounded wildcard - Nếu một biến có thể được truy cập bằng phương thức lớp Object thì hãy sử dụng một ký tự đại diện không liên kết.

  • No wildcard - Nếu mã đang truy cập biến trong cả hai inout danh mục sau đó không sử dụng ký tự đại diện.

Thí dụ

Ví dụ sau minh họa các khái niệm đã đề cập ở trên.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   //Upper bound wildcard
   //in category
   public static void deleteCat(List<? extends Cat> catList, Cat cat) {
      catList.remove(cat);
      System.out.println("Cat Removed");
   }

   //Lower bound wildcard
   //out category
   public static void addCat(List<? super RedCat> catList) {
      catList.add(new RedCat("Red Cat"));
      System.out.println("Cat Added");
   }

   //Unbounded wildcard
   //Using Object method toString()
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String[] args) {

      List<Animal> animalList= new ArrayList<Animal>();
      List<RedCat> redCatList= new ArrayList<RedCat>();

      //add list of super class Animal of Cat class
      addCat(animalList);
      //add list of Cat class
      addCat(redCatList);  
      addCat(redCatList);  

      //print all animals
      printAll(animalList);
      printAll(redCatList);

      Cat cat = redCatList.get(0);
      //delete cat
      deleteCat(redCatList, cat);
      printAll(redCatList); 
   }
}

class Animal {
   String name;
   Animal(String name) { 
      this.name = name;
   }
   public String toString() { 
      return name;
   }
}

class Cat extends Animal { 
   Cat(String name) {
      super(name);
   }
}

class RedCat extends Cat {
   RedCat(String name) {
      super(name);
   }
}

class Dog extends Animal {
   Dog(String name) {
      super(name);
   }
}

Điều này sẽ tạo ra kết quả sau:

Cat Added
Cat Added
Cat Added
Red Cat 
Red Cat 
Red Cat 
Cat Removed
Red Cat

Generics được sử dụng để kiểm tra kiểu chặt chẽ hơn tại thời điểm biên dịch và cung cấp một chương trình chung. Để thực hiện hành vi chung, trình biên dịch java áp dụng kiểu xóa. Xóa kiểu là một quá trình trong đó trình biên dịch thay thế một tham số chung bằng lớp thực tế hoặc phương thức cầu nối. Khi xóa kiểu, trình biên dịch đảm bảo rằng không có lớp bổ sung nào được tạo và không có chi phí thời gian chạy.

Nhập quy tắc xóa

  • Thay thế các tham số kiểu trong kiểu chung bằng liên kết của chúng nếu các tham số kiểu bị giới hạn được sử dụng.

  • Thay thế các tham số kiểu trong kiểu chung bằng Đối tượng nếu các tham số kiểu không liên kết được sử dụng.

  • Chèn phôi loại để bảo vệ an toàn cho loại.

  • Tạo các phương thức cầu nối để giữ tính đa hình trong các kiểu chung mở rộng.

Trình biên dịch Java thay thế các tham số kiểu trong kiểu chung bằng liên kết của chúng nếu các tham số kiểu bị giới hạn được sử dụng.

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<Double> doubleBox = new Box<Double>();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box<T extends Number> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Trong trường hợp này, trình biên dịch java sẽ thay thế T bằng lớp Số và sau khi xóa kiểu, trình biên dịch sẽ tạo bytecode cho đoạn mã sau.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box doubleBox = new Box();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box {
   private Number t;

   public void add(Number t) {
      this.t = t;
   }

   public Number get() {
      return t;
   }   
}

Trong cả hai trường hợp, kết quả là như nhau -

Đầu ra

Integer Value :10
Double Value :10.0

Trình biên dịch Java thay thế các tham số kiểu trong kiểu chung bằng Đối tượng nếu các tham số kiểu không liên kết được sử dụng.

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Trong trường hợp này, trình biên dịch java sẽ thay thế T bằng lớp Đối tượng và sau khi xóa kiểu, trình biên dịch sẽ tạo bytecode cho đoạn mã sau.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }   
}

Trong cả hai trường hợp, kết quả là như nhau -

Đầu ra

Integer Value :10
String Value :Hello World

Trình biên dịch Java thay thế các tham số kiểu trong kiểu chung bằng Đối tượng nếu các tham số kiểu không bị ràng buộc được sử dụng và với kiểu nếu tham số bị ràng buộc được sử dụng làm tham số phương thức.

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
   
   private static <T extends Box> void printBox(T box) {
      System.out.println("Integer Value :" + box.get());
   }
   
   private static <T> void printBox1(T box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Trong trường hợp này, trình biên dịch java sẽ thay thế T bằng lớp Đối tượng và sau khi xóa kiểu, trình biên dịch sẽ tạo bytecode cho đoạn mã sau.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
	
   //Bounded Types Erasure
   private static void printBox(Box box) {
      System.out.println("Integer Value :" + box.get());
   }
	
   //Unbounded Types Erasure
   private static void printBox1(Object box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }   
}

Trong cả hai trường hợp, kết quả là như nhau -

Đầu ra

Integer Value :10
String Value :Hello World

Sử dụng generic, các kiểu nguyên thủy không thể được chuyển làm tham số kiểu. Trong ví dụ dưới đây, nếu chúng ta truyền kiểu nguyên thủy int cho lớp hộp, thì trình biên dịch sẽ phàn nàn. Để giảm thiểu tương tự, chúng ta cần truyền đối tượng Integer thay vì kiểu nguyên thủy int.

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();

      //compiler errror
      //ReferenceType
      //- Syntax error, insert "Dimensions" to complete
      ReferenceType
      //Box<int> stringBox = new Box<int>();

      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Điều này sẽ tạo ra kết quả sau:

Đầu ra

Value: 10

Tham số kiểu không thể được sử dụng để khởi tạo đối tượng của nó bên trong một phương thức.

public static <T> void add(Box<T> box) {
   //compiler error
   //Cannot instantiate the type T
   //T item = new T();  
   //box.add(item);
}

Để đạt được chức năng như vậy, hãy sử dụng phản chiếu.

public static <T> void add(Box<T> box, Class<T> clazz) 
   throws InstantiationException, IllegalAccessException{
   T item = clazz.newInstance();   // OK
   box.add(item);
   System.out.println("Item added.");
}

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) 
      throws InstantiationException, IllegalAccessException {
      Box<String> stringBox = new Box<String>();
      add(stringBox, String.class);
   }  

   public static <T> void add(Box<T> box) {
      //compiler error
      //Cannot instantiate the type T
      //T item = new T();  
      //box.add(item);
   }

   public static <T> void add(Box<T> box, Class<T> clazz) 
      throws InstantiationException, IllegalAccessException{
      T item = clazz.newInstance();   // OK
      box.add(item);
      System.out.println("Item added.");
   }   
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Điều này sẽ tạo ra kết quả sau:

Item added.

Sử dụng generic, các tham số kiểu không được phép là tĩnh. Vì biến tĩnh được chia sẻ giữa các đối tượng nên trình biên dịch không thể xác định kiểu nào được sử dụng. Hãy xem xét ví dụ sau nếu các tham số kiểu tĩnh được cho phép.

Thí dụ

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
	  Box<String> stringBox = new Box<String>();
	  
      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   //compiler error
   private static T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

Vì stringBox và integerBox đều có biến kiểu tĩnh nhìn chằm chằm nên không thể xác định được kiểu của nó. Do đó, các tham số kiểu tĩnh không được phép.

Không cho phép truyền đến một kiểu được tham số hóa trừ khi nó được tham số hóa bằng các ký tự đại diện không bị ràng buộc.

Box<Integer> integerBox = new Box<Integer>();
Box<Number> numberBox = new Box<Number>();
//Compiler Error: Cannot cast from Box<Number> to Box<Integer>
integerBox = (Box<Integer>)numberBox;

Để đạt được điều tương tự, có thể sử dụng các ký tự đại diện không bị ràng buộc.

private static void add(Box<?> box) {
   Box<Integer> integerBox = (Box<Integer>)box;
}

Bởi vì trình biên dịch sử dụng tính năng xóa kiểu, thời gian chạy không theo dõi các tham số kiểu, do đó, sự khác biệt trong thời gian chạy giữa Box <Integer> và Box <String> không thể được xác minh bằng toán tử instanceOf.

Box<Integer> integerBox = new Box<Integer>();

//Compiler Error:
//Cannot perform instanceof check against 
//parameterized type Box<Integer>. 
//Use the form Box<?> instead since further 
//generic type information will be erased at runtime
if(integerBox instanceof Box<Integer>) { }

Mảng có kiểu tham số hóa không được phép.

//Cannot create a generic array of Box<Integer>
Box<Integer>[] arrayOfLists = new Box<Integer>[2];

Bởi vì trình biên dịch sử dụng tính năng xóa kiểu, tham số kiểu được thay thế bằng Đối tượng và người dùng có thể thêm bất kỳ loại đối tượng nào vào mảng. Và trong thời gian chạy, mã sẽ không thể ném ArrayStoreException.

// compiler error, but if it is allowed
Object[] stringBoxes = new Box<String>[];
  
// OK
stringBoxes[0] = new Box<String>();  

// An ArrayStoreException should be thrown,
//but the runtime can't detect it.
stringBoxes[1] = new Box<Integer>();

Một lớp chung không được phép mở rộng lớp Throwable một cách trực tiếp hoặc gián tiếp.

//The generic class Box<T> may not subclass java.lang.Throwable
class Box<T> extends Exception {}

//The generic class Box<T> may not subclass java.lang.Throwable
class Box1<T> extends Throwable {}

Một phương thức không được phép bắt một phiên bản của tham số kiểu.

public static <T extends Exception, J> 
   void execute(List<J> jobs) {
      try {
         for (J job : jobs) {}
  
         // compile-time error
         //Cannot use the type parameter T in a catch block
      } catch (T e) { 
         // ...
   }
}

Các tham số kiểu được cho phép trong mệnh đề ném.

class Box<T extends Exception>  {
   private int t;

   public void add(int t) throws T {
      this.t = t;
   }

   public int get() {
      return t;
   }   
}

Một lớp không được phép có hai phương thức được nạp chồng có thể có cùng một chữ ký sau khi xóa kiểu.

class Box  {
   //Compiler error
   //Erasure of method print(List<String>) 
   //is the same as another method in type Box
   public void print(List<String> stringList) { }
   public void print(List<Integer> integerList) { }
}