+4

Optional trong Java - cách sử dụng và khi nào nên sử dụng

Trước khi đi vào bài viết, gửi tới các bạn lời chúc sức khỏe cho một năm mới 2024 thật cháy, công việc vừa ý, túi tiền nặng ký nhé ♥️😘

Trong lịch sử phát triển của Java, sự xuất hiện của Java 8 đã đánh dấu một bước ngoặt quan trọng với sự giới thiệu của Optional, một cơ chế mới để xử lý an toàn với giá trị null. Đối mặt với thách thức lâu dài của việc quản lý giá trị null, Optional đã mở ra một hướng tiếp cận hoàn toàn mới, giúp lập trình viên tránh xa các lỗi NullPointerException và nâng cao chất lượng của mã nguồn. Tuy nhiên, không phải lúc nào việc sử dụng Optional cũng là lựa chọn tối ưu. Bài viết này sẽ đưa bạn đi từ cơ bản đến nâng cao trong việc sử dụng Optional trong Java, giúp bạn hiểu rõ hơn về cách thức hoạt động của nó và những tình huống nên hoặc không nên sử dụng tính năng này. Hãy cùng khám phá nhé

Nội dung của bài học mình thấy thường xuyên được hỏi trong các buổi phỏng vấn xin việc. Chủ yếu nhà tuyển dụng rất muốn biết liệu bạn có thể hiểu được Optional tới đâu cũng như tại sao cần phải sử dụng nó. Nên các bạn hãy lưu ý và note lại những ý chính cho riêng mình để tự tin hơn nhé 🚀🚀

A. Tổng quan về Optional trong Java

1. Khái niệm

Optional trong Java 8 được thiết kế để cung cấp một cách tiếp cận tốt hơn cho việc xử lý giá trị có thể là null.

Mục đích chính sử dụng : Optional được thiết kế để xử lý tình huống khi một giá trị có thể vắng mặt. Nó giúp lập trình viên rõ ràng biểu thị về khả năng vắng mặt của giá trị, làm cho code trở nên dễ đọc và dễ bảo trì hơn. Giúp giảm thiểu rủi ro của các lỗi NullPointerException trong mã nguồn.

Các cách để khởi tạo Optional:

1. Optional.empty()

  • Phương thức này tạo ra một đối tượng Optional trống, không chứa giá trị nào. Nó thường được sử dụng để biểu thị sự vắng mặt của một giá trị.

    Optional<String> emptyOptional = Optional.empty();

2. Optional.empty()

  • Phương thức này tạo ra một đối tượng Optional chứa một giá trị không null. Nếu bạn cố gắng truyền null vào Optional.of, nó sẽ ném ra một NullPointerException

    Optional<String> optionalWithNonNullValue = Optional.of("Hello");

3. Optional.ofNullable(value)

  • Phương thức này tạo ra một đối tượng Optional từ một giá trị có thể là null. Nếu giá trị đó là null, thì nó trả về một Optional trống; nếu không, nó sẽ tạo ra một Optional chứa giá trị đó.

    String nullableString = getNullableString(); // Giả sử hàm này trả về một chuỗi có thể là null
    Optional<String> optionalFromNullable = Optional.ofNullable(nullableString);
    

Ghi chú quan trọng:

  • Khi sử dụng Optional.of, bạn cần chắc chắn rằng đối số không phải là null để tránh NullPointerException.
  • Sử dụng Optional.ofNullable khi bạn không chắc chắn về tính không null của đối số.
  • Optional.empty thường được sử dụng trong các phương thức mà bạn muốn rõ ràng trả về một giá trị "không có".

2. Cách sử dụng

  • Cách sử dụng của Optional cũng như các phương thức mà nó cung cấp, đi kèm với những ví dụ thực tế đã được mình trình bài trong bài viết này Optional trong Java - làm chủ 15 phương thức trong 20s. Các bạn bớt chút thời gian để đọc lại nhé, vì cả hai bài là sự bổ trợ lẫn nhau. Các bạn có thể đọc bài nào trước cũng được, nhưng nắm rõ được kiến thức của cả hai bài viết sẽ gúp các bạn tự tin hơn rất nhiều trong việc sử dụng Optional đấy.

B. Khi nào nên sử dụng

Mình biết có rất nhiều bạn thắc mắc là trước giờ tôi vẫn có thể check Null bằng cách truyền thống, cũng không mất quá nhiều thời gian, không hề phức tạp vậy tại sao tôi phải sử dụng Optional làm gì. Đại loại những gì mà các bạn nghĩ trong đầu sẽ giống như vậy

image.png

Thế nên bây giờ các bạn hãy cùng mình trả lời cho câu hỏi bên trên, và đó cũng là nội dung chính của chủ đề ngày hôm nay nhé

1. Những lý do bạn nên sử dụng Optional

  1. Khi bạn muốn biểu thị rằng một giá trị có thể không tồn tại: Sử dụng Optional trong API của bạn giúp người dùng biết rằng kết quả có thể không tồn tại. Điều này rõ ràng hơn so với việc trả về null

    public Optional<String> findNameById(String id) {
        // giả sử phương thức này tìm kiếm tên dựa trên id
        if (/* id tồn tại */) {
            return Optional.of("Tên tìm được");
        } else {
            return Optional.empty();
        }
    }
    
  2. Khi muốn cung cấp một cách xử lý rõ ràng cho trường hợp không có giá trị: Optional cung cấp các phương thức như orElse, orElseGet, orElseThrow cho phép xử lý tình huống không có giá trị một cách rõ ràng và linh hoạt.

    String name = findNameById("123").orElse("Tên mặc định");

  3. Làm giảm nguy cơ NullPointerException: Optional giúp giảm nguy cơ gặp phải NullPointerException bởi vì nó yêu cầu người dùng phải xử lý rõ ràng trường hợp không có giá trị.

  4. Tăng cường khả năng sử dụng với Lambda và Stream API: Optional hoạt động tốt với các tính năng mới của Java 8 như Lambda và Stream API. Ví dụ, bạn có thể dễ dàng chuyển đổi một Optional thành một stream, áp dụng các thao tác trên stream và sau đó chuyển ngược lại thành Optional.

    Optional<String> optionalName = ...
    optionalName.stream().filter(name -> name.length() > 3).forEach(System.out::println);
    
  5. Hỗ trợ lập trình hàm (Functional Programming): Optional hỗ trợ các phương thức như map, flatMap, filter, giúp cho việc xử lý dữ liệu trở nên gọn gàng và chức năng hơn, phù hợp với lối lập trình hàm.

  6. Phòng tránh lỗi logic trong code: Khi sử dụng cách tiếp cận truyền thống, có thể phát sinh lỗi logic do quên kiểm tra null. Optional bắt buộc lập trình viên phải xử lý trường hợp không có giá trị, từ đó giúp phòng tránh những lỗi này.

  7. Tạo ra code dễ đọc và bảo trì hơn: Với Optional, code thường ngắn gọn và rõ ràng hơn. Điều này làm cho việc đọc và bảo trì code trở nên dễ dàng hơn.

2. Khi nào không nên sử dụng Optional

  1. Trong tham số của phương thức: Sử dụng Optional làm tham số phương thức thường không khuyến khích vì nó làm tăng độ phức tạp của API và không cải thiện được tính rõ ràng.

    public void doSomething(Optional<String> name) {
        // không nên sử dụng Optional như một tham số
    }
    
  2. Trong lớp trường (class fields): Sử dụng Optional như là một trường trong lớp không phải là một lựa chọn tốt do các hạn chế về hiệu suất và sự không phù hợp với việc serialization.

  3. Khi bạn luôn có một giá trị mặc định: Nếu bạn biết rằng luôn luôn có một giá trị mặc định hợp lý, không cần thiết phải sử dụng Optional. Trong trường hợp này, trả về giá trị mặc định sẽ đơn giản và rõ ràng hơn.

3. Việc sử dụng Optional có làm giảm hiệu suất so với phương pháp truyền thống

Sử dụng Optional trong Java có thể ảnh hưởng đến hiệu suất so với cách tiếp cận truyền thống, nhưng mức độ ảnh hưởng này thường khá nhỏ và phụ thuộc vào bối cảnh cụ thể của ứng dụng. Dưới đây là một số điểm cần lưu ý về hiệu suất khi sử dụng Optional:

  1. Tạo thêm đối tượng: Optional là một container object, và việc sử dụng nó sẽ tạo ra thêm đối tượng trong heap. Điều này có thể gây ra một lượng nhỏ overhead về bộ nhớ và thời gian xử lý, đặc biệt nếu Optional được sử dụng một cách rộng rãi trong một hệ thống lớn hoặc trong các vòng lặp với số lần lặp lớn.

  2. Overhead do bao bọc: Mỗi lần bạn sử dụng Optional, bạn đang bao bọc một giá trị (hoặc không có giá trị) trong một đối tượng khác. Điều này cần thêm thời gian xử lý để truy cập đến giá trị thực tế bên trong. ("overhead" là một thuật ngữ dùng để mô tả chi phí hoặc gánh nặng mà một quá trình nào đó đặt lên hệ thống hoặc chương trình)

  3. Chuỗi phương thức: Optional thường được sử dụng với chuỗi các phương thức như map, flatMap, orElse, v.v. Mặc dù đây là một cách tiếp cận lập trình hàm rất mạnh mẽ và gọn gàng, nhưng nó cũng có thể tạo ra overhead nhất định so với việc sử dụng điều kiện kiểm tra null trực tiếp.

  4. Khả năng tối ưu hóa của JVM: Java Virtual Machine (JVM) và các trình biên dịch hiện đại có khả năng tối ưu hóa mã rất mạnh mẽ. Trong nhiều trường hợp, sự khác biệt về hiệu suất giữa việc sử dụng Optional và kiểm tra null truyền thống có thể không đáng kể hoặc không thể nhận biết được.

  5. Đọc và bảo trì code: Trong khi Optional có thể tạo ra một chút overhead về hiệu suất, nó thường mang lại lợi ích lớn hơn về mặt rõ ràng của code và dễ dàng bảo trì. Trong nhiều trường hợp, lợi ích này có thể quan trọng hơn so với sự tinh chỉnh nhỏ về hiệu suất.

  6. Kết luận: Trong hầu hết các ứng dụng, sự ảnh hưởng về hiệu suất của việc sử dụng Optional là nhỏ và thường không đáng kể so với lợi ích mà nó mang lại về mặt rõ ràng của code và khả năng bảo trì. Tuy nhiên, trong các ứng dụng cực kỳ nhạy cảm với hiệu suất hoặc khi xử lý một lượng lớn dữ liệu trong các vòng lặp nhanh, cần cân nhắc kỹ lưỡng trước khi sử dụng Optional.

C. Kết luận

Optional không phải là giải pháp cho mọi tình huống và không nên được sử dụng một cách quá mức, nó cung cấp một cách tiếp cận hiện đại, rõ ràng và linh hoạt hơn cho việc xử lý giá trị có thể không tồn tại, đặc biệt khi làm việc với Java 8 trở lên. Nhưng thành thạo Optional là một trong những kĩ năng rất cần thiết cho bất cứ lập trình viên Java hiện đại nào, vì vậy hãy thực hành và áp dụng vào những dự án hay bài tập thực tế ngay khi có thể các bạn nhé. Chúc các bạn thành công


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí