Function trong Java 8

1. Giới thiệu Function<T, R>

Java 8 cung cấp sẵn cho chúng ta rất nhiều Functional Interface và Function<T, R> là một trong số đó. Cũng giống như những functional interface khác, Function<T, R> có thể sử dụng cho lambda expression hoặc method reference cho một mục đích cụ thể nào đó. Function<T,R> chỉ có một method trừu tượng duy nhất chấp nhận một tham số đầu vào, và method trả về một đối tượng khác.

Mục đích chính của Function là giúp chúng ta dễ dàng chuyển một đối tượng từ kiểu dữ liệu này sang kiểu dữ liệu khác.

Interface Function được khai báo trong package java.util.function như sau:

Trong đó:

  • R apply(T t) : là một phương thức trừu tượng có thể được sử dụng với lambda expression hoặc method reference cho một mục đích cụ thể nào đó.
  • Phương thức apply() thực hiện một hành động cụ thể trên đối số đã cho và trả về một đối khác.

Interface Function còn cung cấp một phương thức mặc định (default method) sau:

  • Function<V, R> compose(Function<? super V, ? extends T> before) : phương thức này trả về một function gộp mà nó đã áp dụng before Function lên đối số đầu vào, sau đó áp dụng function lên dữ liệu đầu ra.
  • Function<T, V> andThen(Function<? super R, ? extends V> after) : phương thức này trả về một Function gộp mà nó đã áp dụng function lên dữ liệu đầu vào và sau đó áp dụng tiếp after function lên kết quả đầu ra.
  • Function<T, T> identity() : phương thức này trả về một function luôn trả về đối số đầu vào của nó.

2. Một số ví dụ

2.1. Sử dụng R apply(T t)

package com.maixuanviet.function;
 
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.function.Function;
 
public class FunctionExample1 {
 
    public static void main(String[] args) {
 
        Function<String, Integer> numberConverter = (str) -> Integer.parseInt(str);
        System.out.println(numberConverter.apply("1")); // 1
 
        Function<LocalDate, String> dateConverter = (d) -> d.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
        System.out.println(dateConverter.apply(LocalDate.now())); // 27/05/2018
    }
}

Trong ví dụ trên tôi đã tạo 2 Function:

  • Function numberConverter : chấp nhận đối số đầu vào là một chuỗi (String), kết quả đầu ra là một số (Integer).
  • Function dateConverter : chấp nhận đối số đầu vào là một LocalDate, kết quả đầu ra là một chuỗi (String) của Date đã được format.

Để nhận được kết quả của Function, chúng ta cần gọi phương thức apply().

2.2. Chuyển một đối tượng này sang một đối tượng khác (object to object)

package com.maixuanviet.function;
 
import java.util.function.Function;
 
class User {
    String name;
    String email;
    String password;
 
    public User(String name, String email, String password) {
        this.name = name;
        this.email = email;
        this.password = password;
    }
}
 
class Member {
    String name;
    String email;
 
    public Member(String name, String email) {
        this.name = name;
        this.email = email;
    }
 
    @Override
    public String toString() {
        return "Member [name=" + name + ", email=" + email + "]";
    }
}
 
public class FunctionExample2 {
 
    public static void main(String[] args) {
 
        Function<User, Member> mapUserToMember = u -> new Member(u.name, u.email);
 
        User user = new User("maixuanviet", "maixuanvietvn@gmail.com", "123");
        Member member = mapUserToMember.apply(user);
        System.out.println(member);
    }
}

Output của chương trình trên:

Member [name=VietMX, email=maixuanviet.com@gmail.com]

2.3. Chuyển một danh sách đối tượng này sang một danh sách đối tượng khác (list object to list object)

package com.maixuanviet.function;
 
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
 
public class FunctionExample3 {
 
    public static void main(String[] args) {
         
        List<User> users = Arrays.asList( // 
            new User("maixuanviet1", "maixuanvietvn1@gmail.com", "123"), // 
            new User("maixuanviet2", "maixuanvietvn2@gmail.com", "124"), // 
            new User("maixuanviet3", "maixuanvietvn3@gmail.com", "125") //  
        );
 
        Function<User, Member> mapUserToMember = u -> new Member(u.name, u.email);
 
        List<Member> members = users.stream()
                .map(mapUserToMember)
                .collect(Collectors.toList());
    }
}

2.4. Sử dụng phương thức mặc định andThen() và compose()

Phương thức andThen() : phương thức này trả về một Function thực hiện hai hành động theo thứ tự, trước tiên là hành động của Function mà phương thức được gọi và theo sau bởi hành động của Function được truyền vào đối số.

Phương thức compose() : phương thức này trả về một Function thực hiện hai hành động theo thứ tự, trước tiên là hành động của Function mà được truyền vào đối số và theo sau bởi hành động của Function gọi phương thức.

Phương thức identity() : phương thức này trả về một Function thực hiện trả về kết quả đúng bằng với đối số được truyền vào.

@FunctionalInterface
public interface Function<T, R> {
 
    R apply(T t);
     
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
     
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
     
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

Ví dụ:

package com.maixuanviet.function;
 
import java.util.function.Function;
 
public class FunctionExample4 {
 
    public static void main(String[] args) {
 
        Function<Integer, Integer> times2 = n -> n * 2;
        Function<Integer, Integer> squared = n -> n * n;
 
        Function<Integer, Integer> andThen = times2.andThen(squared);
        System.out.println("Using andThen: " + andThen.apply(5)); // 100
 
        Function<Integer, Integer> compose = times2.compose(squared);
        System.out.println("Using compose: " + compose.apply(5)); // 50
    }
}

2.5. Sử dụng Function với các lớp cho kiểu dữ liệu nguyên thủy (primitive type)

Java 8 cung cấp một số Interface Function cho các wrapper class của kiểu dữ liệu nguyên thủy như sau:

  • IntFunction : chấp nhận một đối số đầu vào kiểu int và kết quả đầu ra là kiểu bất kỳ.
  • LongFunction : chấp nhận một đối số đầu vào kiểu long và kết quả đầu ra là kiểu bất kỳ.
  • DoubleFunction : chấp nhận một đối số đầu vào kiểu double và kết quả đầu ra là kiểu bất kỳ.
@FunctionalInterface
public interface IntFunction<R> {
    R apply(int value);
}
 
@FunctionalInterface
public interface LongFunction<R> {
    R apply(long value);
}
 
@FunctionalInterface
public interface DoubleFunction<R> {
    R apply(double value);
}

Ví dụ:

package com.maixuanviet.function;
 
import java.util.function.DoubleFunction;
import java.util.function.IntFunction;
import java.util.function.LongFunction;
 
public class FunctionExample5 {
 
    public static void main(String[] args) {
 
        IntFunction<String> ifunc = (x) -> Integer.toString(x * x);
        LongFunction<String> lfunc = (x) -> Long.toString(x * x);
        DoubleFunction<String> dfunc = (x) -> Double.toString(x * x);
 
        System.out.println(ifunc.apply(3)); // 9
        System.out.println(lfunc.apply(5)); // 25
        System.out.println(dfunc.apply(10)); // 100.0
    }
}

2.6. Sử dụng Function 2 đối số với BiFunction

Như đã nói ở phần trên Function chỉ chấp nhận 1 đối số đầu vào, để có thể sử dụng Function với 2 đối số đầu vào chúng ta sử dụng Interface BiFunction.

Về cơ bản, interface BiFunction  không khác biệt so với Function , ngoại trừ nó chấp nhận 2 đối số đầu vào.

Ví dụ:

package com.maixuanviet.function;
 
import java.util.function.BiFunction;
import java.util.function.Function;
 
public class BiFunctionExample {
 
    public static void main(String[] args) {
 
        BiFunction<String, String, String> function1 = (s1, s2) -> s1 + s2;
        System.out.println(function1.apply("maixuanviet", ".com")); // maixuanviet.com
 
        BiFunction<Integer, Integer, Integer> function2 = (a, b) -> a + b;
        System.out.println(function2.apply(1, 2)); // 3
 
        BiFunction<Integer, Integer, Integer> times2 = (a, b) -> a + b;
        Function<Integer, Integer> squared = (n) -> n * n;
 
        BiFunction<Integer, Integer, Integer> andThen = times2.andThen(squared);
        System.out.println("Using andThen: " + andThen.apply(5, 2)); // 49
    }
}

2 Trackbacks / Pingbacks

  1. Sắp xếp trong Java 8 - Blog của VietMX
  2. Refactoring Design Pattern với tính năng mới trong Java 8 - Blog của VietMX

Comments are closed.