Vọc vạch, nghịch nghợm
Vọc vạch, nghịch nghợm

Nếu đã là fan cứng của Java thì hẳn các bạn cũng biết là kể từ Java 8, Java đã hỗ trợ cho functional programming. Nhưng với những ngôn ngữ OOP không được thiết kế dành cho functional programming (như Java 7) thì thế nào? Có những thư viện mã nguồn mở hỗ trợ lập trình Java theo kiểu functional programming như functionaljava.org. Tuy nhiên, ở bài viết này mình chia sẻ cách áp dụng mà không dùng đến thư viện. Nếu các muốn tìm hiểu về functional programming trên Java 8 thì có thể tự tìm hiểu trên Google, hoặc theo link này.

Mục tiêu của bài viết.

Bài viết này không phải là tutorial, cũng không phải là chia sẻ kiến thức theo kiểu “sách giáo khoa”. Bài viết này chỉ chia sẻ về việc vọc code của mình. Việc vọc code này chỉ giúp chúng ta dễ hình dung hơn về functional programing, và cách mà chúng hoạt động trên Java 8. Chính vì vậy, mình không khuyến khích các bạn áp dụng những chia sẻ này vào thực tế.

Các khái niệm cơ bản của Functional Programming

Áp dụng các khái niệm cơ bản của functional Programming vào Java 7:

First-class functions

Java không phải là ngôn ngữ first-class functions. Chúng ta có thể coi method trong Java là function, tuy nhiên, ở Java 7, chúng ta chưa có cú pháp method reference. Mặc dù Java 8 đã hỗ trợ method reference, nhưng bản chất của nó vẫn là một object (implement functional interface). Tương tự như vậy, ở Java 7, chúng ta có cũng thể coi các object như là các method reference của các method mà nó implement.

Java 8 có hỗ trợ cú pháp lambda để định nghĩa một “function” mới. Trong khi đó Java 7 thì không. Vậy thì ở Java 7 chúng ta sử sụng anonymous inner class để thay cho cú pháp lambda.

Pure functions

Method trong Java có thể là pure function hoặc không. Nếu muốn là pure function, thì chúng ta phải tự giới hạn trong code, sao cho method của chúng ta thõa mãn điều kiện của một pure function là được.

Higher order functions

Khi đã có function (thực chất là Java object), thì những method chấp nhận tham số là function, được xem là higher order function.

Functional interfaces

Ở Java 8, chúng ta có khái niệm functional interface. Ở Java 7, chúng ta cũng có thể dùng khái niệm đó. Tuy nhiên ở Java 7 chúng ta không có sẵn annotation  @FunctionalInterface. Ngoài ra Java 8 cũng định nghĩa sẵn các functional interface như Function, Predicate, Consumer… Trên Java 7, chúng ta cũng có thể tự định nghĩa những interface tương tự.

Ta có thể định nghĩa higher order function, với tham số là functional interface như sau.

Khi sử dụng:

Code bên trên tương đương với việc dùng lambda ở Java 8:

Functional Composition

Java 8 hỗ trợ interface default method và interface static method. Do đó ở Predicate, ngoài test, chúng ta còn có and, or, negative, isEqual; Hay ở Function, chúng ta có compose, andThen, identity… Vậy giải pháp thay thế ở Java 7 là gì?

Dùng util method

Trong Java 7 thì chúng ta có thể định nghĩa các util method để thay thế cho interface default method và interface static method.

Lấy ví dụ với phương thức check như sau:

Khi sử dụng (import static phương thức and):

Nếu phương thức check dùng Java 8 Predicate thay vì MyPredicate thì code Java 8 tương đương sẽ là:

Nhưng khoan. Kiểu của nonNull là Predicate<String>. Thế khi kiểu của nó là Predicate<Object> thì sao? Thì chơi chiêu thôi, thế này nè:

Dùng Inner class, giả default method

Chúng ta cũng có thể giả default method bằng cách tạo một interface có các default method giả. Dùng class Creator để tạo instance cho cho interface đó. Tương tự với static method, chúng ta cũng có thể đưa method vào inner class.

Sửa lại ví dụ formatMoney lúc nãy:

Lúc này ta có thể sử dụng nó như sau (import static phương thức createFunction):

Nếu dùng lambda và reference method của Java 8 thì code sẽ dễ nhìn hơn:

Stream API

Khi đã xác định được cách áp dụng functional programming vào Java 7 thì chúng ta cũng có thể áp dụng để implement một thứ gì đó na ná với Stream API. Ở đây, mình đã thử định nghĩa interface MyStream, có một số phương thức giống với  Java 8 Stream, bao gồm  filtermapflatMapreducecountforEach.

Interface MyStream được định nghĩa như sau:

Mình kế thừa lớp ArrayList để làm nguồn cung cấp cho stream.

ArrayListStream sẽ implement MyStream. Tuy nhiên, để giúp tái sử dụng code tốt hơn, mình định nghĩa  MyAbstractStream  implement MyStream, rồi cho  ArrayListStream kế thừa  MyAbstractStream. Class  MyAbstractStream mình impelent như sau:

Các class FilteredStream, MappedStream, FlatMappedStream được implement như sau:

Vậy là chúng ta đã có thể sử dụng MyStream và các phương thức của nó rồi.

Lưu ý: Những đoạn code này là những thứ na ná Java 8 Stream API. Nó không hoàn toàn giống Stream API và Stream API cũng không phải được implement theo cách này.

Nếu muốn giống Stream API hơn một chút nữa. Thì có thể làm cho nó có thứ na ná thế này:
Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed
Như thế này:

Kết

Việc áp dụng functional programming cho ngôn ngữ lập trình hướng đối tượng là khả thi. Tuy nhiên việc này sẽ tiêu tốn nhiều thời gian viết code và khả năng tối ưu performance cũng sẽ bị hạn chế. Bởi vì cú pháp ngôn ngữ, và nền tảng không được thiết kế cho functional programming, cũng như thiếu các thư viện, API liên quan đến functional programming.

Bài viết này không định hướng “áp dụng” cho thực tế, mà chỉ định hướng “áp dụng” cho vọc code mà thôi. Chúc các bạn vọc code vui vẻ. Mọi ý kiến đóng góp hoặc thắc mắc, vui lòng comment hoặc inbox m.me/DiepEsc.