Generics

·

6 min read

Dưới đây là 20 câu hỏi phỏng vấn Java tập trung vào các chủ đề Generics bao gồm Why Generics, Raw Types, Generic Types, Bounded Types, Type Inference, Erasure, Bridge Methods, Wildcard, The Get & the Put Principle, và Reification.

Why Generics

  1. Câu hỏi: Tại sao Generics được giới thiệu trong Java và lợi ích của chúng là gì?

    • Trả lời: Generics được giới thiệu để cung cấp tính an toàn kiểu (type safety) và loại bỏ các lỗi runtime liên quan đến kiểu. Chúng cho phép các lớp, interface, và phương thức làm việc với các kiểu dữ liệu khác nhau mà không cần kiểu cụ thể, đồng thời cung cấp kiểm tra kiểu tại thời điểm biên dịch (compile-time).
  2. Câu hỏi: Sự khác biệt giữa việc sử dụng Generics và việc sử dụng Object là gì?

    • Trả lời: Sử dụng Generics cung cấp kiểm tra kiểu tại thời điểm biên dịch, ngăn chặn lỗi cast tại runtime và cải thiện sự rõ ràng và an toàn của mã. Sử dụng Object yêu cầu ép kiểu thủ công, dễ dẫn đến lỗi runtime.

Raw Types

  1. Câu hỏi: Raw type là gì và tại sao chúng không được khuyến khích sử dụng?

    • Trả lời: Raw type là phiên bản không có tham số của một generic class hoặc interface. Chúng không được khuyến khích sử dụng vì chúng bỏ qua kiểm tra kiểu tại thời điểm biên dịch và có thể gây ra lỗi runtime.
  2. Câu hỏi: Hãy cho ví dụ về raw type và cách sử dụng đúng Generics thay vì raw type.

    • Trả lời:

        // Raw type
        List list = new ArrayList();
        list.add("string");
      
        // Sử dụng Generics
        List<String> list = new ArrayList<>();
        list.add("string");
      

Generic Types

  1. Câu hỏi: Generic type là gì? Hãy cho ví dụ về việc khai báo và sử dụng một generic class.

    • Trả lời: Generic type là một lớp hoặc interface có thể làm việc với các kiểu dữ liệu khác nhau thông qua tham số hóa. Ví dụ:

        public class Box<T> {
            private T item;
      
            public void setItem(T item) {
                this.item = item;
            }
      
            public T getItem() {
                return item;
            }
        }
      
        Box<String> stringBox = new Box<>();
        stringBox.setItem("Hello");
      
  2. Câu hỏi: Hãy giải thích sự khác biệt giữa T, E, K, V trong Generics.

    • Trả lời: Các ký tự này là convention cho tên kiểu generics:

      • T (Type): Đại diện cho một kiểu dữ liệu.

      • E (Element): Thường được sử dụng trong các collection như List, Set.

      • K (Key): Đại diện cho một khóa trong các cấu trúc dữ liệu như Map.

      • V (Value): Đại diện cho một giá trị trong các cấu trúc dữ liệu như Map.

Bounded Types

  1. Câu hỏi: Bounded type parameter là gì? Hãy cho ví dụ về bounded type với từ khóa extends.

    • Trả lời: Bounded type parameter giới hạn kiểu dữ liệu mà tham số hóa có thể chấp nhận. Ví dụ:

        public <T extends Number> void print(T number) {
            System.out.println(number);
        }
      
  2. Câu hỏi: Hãy cho ví dụ về bounded type với từ khóa super.

    • Trả lời:

        public <T> void addToList(List<? super T> list, T item) {
            list.add(item);
        }
      

Type Inference

  1. Câu hỏi: Type inference là gì và nó hoạt động như thế nào trong Java?

    • Trả lời: Type inference là khả năng của trình biên dịch tự động xác định kiểu dữ liệu của tham số hóa. Nó hoạt động thông qua phân tích ngữ cảnh của mã. Ví dụ:

        List<String> list = new ArrayList<>();
      
  2. Câu hỏi: Hãy giải thích diamond operator (<>) và cách nó giúp type inference.

    • Trả lời: Diamond operator (<>) được giới thiệu trong Java 7 để giảm sự lặp lại của kiểu dữ liệu khi khởi tạo generics. Nó cho phép trình biên dịch suy luận kiểu từ ngữ cảnh. Ví dụ:

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

Erasure

  1. Câu hỏi: Type erasure là gì và tại sao Java sử dụng nó?

    • Trả lời: Type erasure là quá trình loại bỏ thông tin kiểu cụ thể tại thời điểm biên dịch, chuyển đổi các generic type thành kiểu Object. Java sử dụng type erasure để duy trì tính tương thích ngược với mã không sử dụng generics.
  2. Câu hỏi: Sự khác biệt giữa generic code và non-generic code sau khi type erasure là gì?

    • Trả lời: Sau type erasure, generic code và non-generic code tương đương nhau về mặt bytecode. Sự khác biệt chính là kiểm tra kiểu tại thời điểm biên dịch, giúp tránh lỗi runtime.

Bridge Methods

  1. Câu hỏi: Bridge method là gì và tại sao chúng được tạo ra trong quá trình biên dịch?

    • Trả lời: Bridge method là các phương thức được trình biên dịch tự động tạo ra để duy trì tính toàn vẹn của tính kế thừa khi sử dụng generics với type erasure. Chúng giúp các phương thức overridden trong lớp con tương thích với lớp cha.
  2. Câu hỏi: Hãy cho ví dụ về tình huống mà bridge method sẽ được tạo ra.

    • Trả lời:

        class Node<T> {
            public T data;
            public Node(T data) {
                this.data = data;
            }
        }
      
        class MyNode extends Node<Integer> {
            public MyNode(Integer data) {
                super(data);
            }
        }
      
        // Trình biên dịch sẽ tạo bridge method cho MyNode
      

Wildcard

  1. Câu hỏi: Wildcard (?) là gì trong Generics và khi nào nên sử dụng nó?

    • Trả lời: Wildcard (?) đại diện cho một kiểu không xác định. Nó được sử dụng khi bạn muốn viết mã mà không yêu cầu một kiểu cụ thể, cho phép tính linh hoạt cao hơn trong việc truyền tham số hóa.
  2. Câu hỏi: Hãy cho ví dụ về việc sử dụng wildcard với extendssuper.

    • Trả lời:

        // Wildcard với extends
        public void process(List<? extends Number> list) {
            for (Number number : list) {
                System.out.println(number);
            }
        }
      
        // Wildcard với super
        public void addToList(List<? super Integer> list) {
            list.add(10);
        }
      

The Get & the Put Principle

  1. Câu hỏi: Hãy giải thích nguyên tắc "Get and Put" trong Generics.

    • Trả lời: Nguyên tắc "Get and Put" quy định rằng bạn nên sử dụng wildcard extends khi bạn chỉ đọc từ một cấu trúc dữ liệu (get), và sử dụng wildcard super khi bạn chỉ ghi vào một cấu trúc dữ liệu (put).
  2. Câu hỏi: Hãy cho ví dụ minh họa nguyên tắc "Get and Put".

    • Trả lời:

        // Sử dụng extends để get
        public void printList(List<? extends Number> list) {
            for (Number number : list) {
                System.out.println(number);
            }
        }
      
        // Sử dụng super để put
        public void addNumbers(List<? super Integer> list) {
            list.add(1);
            list.add(2);
        }
      

Reification

  1. Câu hỏi: Reification là gì và tại sao Java không hỗ trợ nó với Generics?

    • Trả lời: Reification là quá trình duy trì thông tin kiểu cụ thể tại runtime. Java không hỗ trợ reification với Generics vì sử dụng type erasure để đảm bảo tính tương thích ngược với mã không sử dụng generics.
  2. Câu hỏi: Hãy giải thích các hạn chế của Generics do việc không hỗ trợ reification.

    • Trả lời: Do không hỗ trợ reification, các hạn chế của Generics bao gồm không thể tạo các đối tượng kiểu cụ thể tại runtime, không thể sử dụng instanceof với các tham số hóa, và không thể tạo mảng của generic types.