Java: Collection và Generic

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Tổng quan

Collection là một đối tượng quản lý một nhóm các đối tượng. API Collection phụ thuộc vào generic để triển khai nó. Ví dụ sau minh họa điều này.

public class GenericArrayListExample {
  public static void main(String[] args) {
    List<Integer> partObj = new ArrayList<>(3);
    partObj.add(new Integer(1010));
    partObj.add(new Integer(2020));
    partObj.add(new Integer(3030));
    System.out.println("Part Numbers are as follows: ");
    Iterator<Integer> value = partObj.iterator();
    while (value.hasNext()) {
      Integer partNumberObj = value.next();
      int partNumber = partNumberObj.intValue();
      System.out.println("" + +partNumber);
    }
  }
}

Hình dưới đây hiển thị output thể hiện việc sử dụng API thu thập và các generic.

Generic 6 GenericArrayListExample

Sử dụng một giá trị không hợp lệ với các kết quả generic dẫn đến lỗi thời gian biên dịch.

Các ký tự đại diện với generic

Các ký tự đại diện được sử dụng để khai báo các kiểu tham số ký tự đại diện. Ký tự đại diện được sử dụng làm đối số cho các trường hợp của kiểu generic. Các ký tự đại diện hữu ích khi không có hoặc chỉ có một chút kiến ​​thức về đối số kiểu của một kiểu được tham số hóa. Có ba loại ký tự đại diện được sử dụng với Generics như dưới đây.

?

Ký tự đại diện '?' đại diện cho một kiểu không xác định trong Generics. Nó biểu thị tập hợp tất cả các kiểu hoặc bất kỳ một kiểu nào. Ví dụ như List<?> có nghĩa là danh sách chứa kiểu đối tượng không xác định. Ký tự đại diện không bị ràng buộc (“?”) được sử dụng làm đối số cho các phần khởi tạo của các kiểu generic. Ký tự đại diện không giới hạn hữu ích trong các trường hợp không cần kiến ​​thức về đối số kiểu của một kiểu tham số hóa.

Ví dụ sau cho thấy ưu điểm của việc sử dụng ký tự đại diện không bị ràng buộc.

public class Paper {
  public void draw(Shape shapeObj) {
    shapeObj.draw(this);
  }

  public void displayAll(List<Shape> shObj) {
    for (Shape s : shObj) {
      s.draw(this);
    }
  }
}

Hãy xem xét rằng lớp Paper chứa một phương thức hiển thị tất cả các hình dạng được biểu diễn dưới dạng danh sách. Nếu signature phương thức của phương thức displayAll() như được chỉ định như trong ví dụ thì nó chỉ có thể được gọi trên danh sách kiểu Shape. Không thể gọi phương thức trên List<Circle>.

? extends Type

Ký tự đại diện giới hạn '? extends Type' đại diện cho một kiểu không xác định là một kiểu con của lớp giới hạn. Từ 'Type' chỉ định một giới hạn trên, trong đó nói rằng giá trị của tham số kiểu phải mở rộng lớp hoặc triển khai interface của lớp giới hạn.

Nó biểu thị một họ các kiểu con của kiểu Type. Ví dụ như List<? extends Number> nghĩa là danh sách đã cho chứa các đối tượng có nguồn gốc từ lớp Number. Nói cách khác, ký tự đại diện được giới hạn bằng cách sử dụng từ khóa extends giới hạn phạm vi loại có thể được chỉ định. Đây là ký tự đại diện hữu ích nhất.

Ví dụ sau hiển thị khai báo của ký tự đại diện giới hạn '? extends Type'.

public void displayAll(List<? extends Shape> shape) {
}

Đoạn mã trên khai báo một danh sách chấp nhận phương thức với bất kỳ loại Shape nào. Phương thức chấp nhận danh sách của bất kỳ lớp nào là lớp con của Shape. Phương thức displayAll() có thể chấp nhận List<Circle> làm đối số của nó.

? super Type

Ký tự đại diện giới hạn '? super Type' đại diện cho một kiểu không xác định là siêu kiểu của lớp bao. Từ 'Type' chỉ định một giới hạn dưới. Ký tự đại diện biểu thị một họ siêu kiểu của kiểu Type. Ví dụ như List<? super Number> có nghĩa là nó có thể là List<Number> hoặc List<Object>.

Ví dụ sau hiển thị việc sử dụng ký tự đại diện giới hạn '? super Type'.

public class NumList {
  public static <T> void copy(List<? super T> destObj, List<? extends T> srcObj) {
    for (int ctr = 0; ctr < srcObj.size(); ctr++) {
      destObj.set(ctr, srcObj.get(ctr));
    }
  }
}

Trong phương thức '<? super T>' chỉ ra rằng đối tượng đích destObj có thể có các phần tử thuộc bất kỳ kiểu nào là supertype của T. Tương tự, câu lệnh '<? extends T>' nghĩa là đối tượng nguồn srcObj có thể có các phần tử thuộc bất kỳ kiểu nào là kiểu con của T. Phương thức copy- All() gọi phương thức copy() với kiểu tham số là Object. Lời gọi phương thức này hoạt động vì srcObj có kiểu List<Object> là chính lớp đó. Đối tượng intObj có kiểu là List<Integer> là một kiểu con của Object.

Hình dưới đây hiển thị các loại ký tự đại diện khác nhau.

Một số wildcard-ký tự đại diện

Ghi chú - Đối với '?  extends Type', các phần tử có thể được truy xuất từ ​​cấu trúc nhưng không thể được thêm.  

Xử lý ngoại lệ với Generics

Các ngoại lệ cung cấp một cơ chế đáng tin cậy để xác định và phản hồi các điều kiện lỗi. Mệnh đề catch có trong câu lệnh try kiểm tra xem ngoại lệ được ném có khớp với kiểu đã cho hay không. Một trình biên dịch không thể đảm bảo rằng các tham số kiểu được chỉ định trong mệnh đề catch khớp với ngoại lệ không rõ nguồn gốc vì một ngoại lệ được ném và bắt tại thời gian chạy. Do đó, mệnh đề catch không thể bao gồm các biến kiểu hoặc ký tự đại diện. Một lớp con của lớp Throwable không thể được tạo thành generic vì không thể bắt một ngoại lệ thời gian chạy với các tham số thời gian biên dịch còn nguyên vẹn.

Trong Generics, biến kiểu có thể được sử dụng trong mệnh đề throws của phương thức. Hình sau hiển thị việc xử lý ngoại lệ với Generics.

Xử lý ngoại lệ với generics

Ví dụ sau thể hiện việc sử dụng kiểu generic với các ngoại lệ.

interface Command<X extends Throwable> {
  public void calculate(Integer arg) throws X;
}

public class ExTest implements Command<ArithmeticException> {
  public void calculate(Integer num) throws ArithmeticException {
    int no = num.valueOf(num);
    System.out.println("Value is: " + (no / 0));
  }
}

Ví dụ trên hiển thị cách sử dụng tính generic trong Ngoại lệ. Đoạn mã trên cũng sử ​​dụng một biến kiểu trong mệnh đề throws của nhận diện phương thức. Việc sử dụng tham số kiểu X cho thấy rằng đoạn mã có thể ném ra một ngoại lệ.

Thừa kế với Generics

Thừa kế là một cơ chế để dẫn xuất các lớp hoặc interface mới từ các lớp hoặc interface hiện có. Lập trình hướng đối tượng cho phép các lớp kế thừa trạng thái và hành vi thường được sử dụng từ các lớp khác.

Các lớp có thể mở rộng các lớp generic và cung cấp giá trị cho các tham số kiểu hoặc thêm các tham số kiểu mới. Một lớp không thể kế thừa từ kiểu tham số. Không thể sử dụng hai phần khởi tạo của cùng một kiểu generic trong kế thừa.

Ví dụ sau thể hiện việc sử dụng generic với sự kế thừa.

class Month<T> {
  T monthObj;

  Month(T obj) {
    monthObj = obj;
  }

  // Return monthObj T getob()
  {
    return monthObj;
  }
}

// A subclass of Month that defines a second type parameter, called V.
class MonthArray<T, V> extends Month<T> {
  V valObj;

  MonthArray(T obj, V obj2) {
    super(obj);
    valObj = obj2;
  }

  V getob2() {
    return valObj;
  }
}

// Create an object of type MonthArray 
public class HierTest {
  public static void main(String args[]) {
    MonthArray<String, Integer> month;
    month = new MonthArray<>("Value is: ",99);
    System.out.print(month.getob());
    System.out.println(month.getob2());
  }
}

Trong ví dụ trên, lớp con MonthArray là thể hiện cụ thể của lớp Month<T>.

Hình dưới hiển thị output.

HierTest

» Tiếp: Khả năng tương tác với Generics
« Trước: Kiểu suy luận (Type Inference)
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!