betacode

Руководство Java Function

  1. Function interface
  2. Function + Method reference
  3. Function + Constructor reference
  4. Function Usages
  5. Function.compose(Function before)
  6. Function.andThen(Function after)
  7. Function.identity()

1. Function interface

В Java 8,Function - это functional interface, который представляет собой оператор, принимающий входное значение и возвращающий значение.
Исходный код interface Function:
Function interface
package java.util.function;

import java.util.Objects;

@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;
    }
}
Например:
FunctionEx1.java
package org.o7planning.ex;

import java.util.function.Function;

public class FunctionEx1 {

    public static void main(String[] args) {
        
        Function<String, Integer> func = (text) -> text.length();
        
        int length = func.apply("Function interface tutorial");
        
        System.out.println("Length: " + length);
    }
}
Output:
Length: 27
Например: Обработка элементов объекта List для создания нового объекта List.
FunctionEx2.java
package org.o7planning.ex;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class FunctionEx2 {

    public static void main(String[] args) {
        
        Function<String, String> func = text -> text.toUpperCase();
        
        List<String> list = Arrays.asList("Java", "C#", "Python");

        List<String> newList = map(func, list);
    
        newList.forEach(System.out::println);
    }
    
    public static <T,R> List<R> map(Function<T,R> mapper, List<T> list) {
        List<R> result = new ArrayList<R>();
        
        for(T t: list)  {
            R r = mapper.apply(t);
            result.add(r);
        }
        return result;
    }
}
Output:
JAVA
C#
PYTHON
Например: Использование Function для преобразования объекта List в объект Map:
FunctionEx3.java
package org.o7planning.ex;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class FunctionEx3 {

    public static void main(String[] args) {
        
        Function<String, Integer> func = text -> text.length();
        
        List<String> list = Arrays.asList("Java", "C#", "Python");

        Map<String, Integer> map = listToMap(func, list);
    
        // @see: Map.forEach(BiConsumer).
        map.forEach((t,r) -> System.out.println(t + " : " + r));
    }
    
    public static <T,R> Map<T,R> listToMap(Function<T,R> mapper, List<T> list) {
        Map<T, R> result = new HashMap<T, R>();
        
        for(T t: list)  {
            R r = mapper.apply(t);
            result.put(t, r);
        }
        return result;
    }
}
Output:
C# : 2
Java : 4
Python : 6

2. Function + Method reference

M.ref example 1:
Если статический метод принимает один параметр и возвращает значение, его ссылка может рассматриваться как Function.
Function_mref_ex1.java
package org.o7planning.ex;

import java.util.function.Function;

public class Function_mref_ex1 {

    public static void main(String[] args) {

        // A method of Math class: public static long round(double)
        Function<Double, Long> func1 = Math::round; // Method reference
        Function<Double, Long> func2 = value -> Math.round(value);

        System.out.println(func1.apply(100.7));
        System.out.println(func2.apply(100.7));
    }
}
Output:
101
101
M.ref example 2:
Если нестатический метод (non-static method), не принимает параметров и возвращает значение, то его ссылку можно рассматривать как Function.
Function_mref_ex2.java
package org.o7planning.ex;

import java.util.function.Function;

public class Function_mref_ex2 {

    public static void main(String[] args) {

        // A method of String class: public int length()
        Function<String, Integer> func1 = String::length; // Method reference
        
        Function<String, Integer> func2 = text -> text.length();

        System.out.println(func1.apply("Java")); // 4
        System.out.println(func2.apply("Java")); // 4
    }
}
M.ref example 3:
Далее, взгляните на класс CurrencyFormatter ниже:
  • Метод CurrencyFormatter.usd(double) имеет параметр типа Double и возвращает String. Таким образом, его ссылку CurrencyFormatter::usd можно рассматривать как Function<Double,String>.
CurrencyFormatter.java
package org.o7planning.tax;

public class CurrencyFormatter {

    // Dollar
    public static String usd(double amount) {
        return "$" + amount;
    }

    // Euro
    public static String euro(double amount) {
        return "€" + amount;
    }

    // Vietnam Dong.
    public static String vnd(double amount) {
        return amount + "VND";
    }
}
TaxCalcExample.java
package org.o7planning.tax;

import java.util.function.Function;

public class TaxCalcExample {

    public static void main(String[] args) {

        double amount = 1000;

        String format = formatCurrency("VN", amount);

        System.out.println("VN: " + format);

        format = formatCurrency("US", amount);

        System.out.println("US: " + format);

        format = formatCurrency("EU", amount);

        System.out.println("EU: " + format);
    }

    public static String formatCurrency(String countryCode, double amount) {
        if ("VN".equals(countryCode)) {
            Function<Double, String> formatter = CurrencyFormatter::vnd; // Method reference
            return _formatCurrency(formatter, amount);
        }
        if ("US".equals(countryCode)) {
            return _formatCurrency(CurrencyFormatter::usd, amount);
        }
        if ("EU".equals(countryCode)) {
            return _formatCurrency(CurrencyFormatter::euro, amount);
        }
        throw new RuntimeException("No formatter for " + countryCode);
    }

    private static String _formatCurrency(Function<Double, String> formatter, double amount) {
        return formatter.apply(amount);
    }
}
Output:
VN: 1000.0VND
US: $1000.0
EU: €1000.0

3. Function + Constructor reference

Как вы знаете, constructor используется для создания объекта, это означает, что он возвращает значение. Таким образом, если constructor имеет один параметр, его ссылка будет рассматриваться как Function.
Student.java
package org.o7planning.cr;

public class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
ConstructorReferenceTest.java
package org.o7planning.cr;

import java.util.function.Function;

public class ConstructorReferenceTest {

    public static void main(String[] args) {
        Function<String, Student> f1 = Student::new; // Constructor Reference
        Function<String, Student> f2 = (name) -> new Student(name); // Lambda Expression

    
        System.out.println(f1.apply("Tom").getName());
        System.out.println(f2.apply("Jerry").getName());
    }
}
Output:
Tom
Jerry

4. Function Usages

Ниже приведен список методов в package java.util, использующих interface Function:
static
<T,U extends Comparable<? super U>>
Comparator<T>
Comparator.comparing(Function<? super T,? extends U> keyExtractor)
static <T,U> Comparator<T>
Comparator.comparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)
V
Hashtable.computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
V
HashMap.computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
default V
Map.computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
<U> Optional<U>
Optional.flatMap(Function<? super T,Optional<U>> mapper)
<U> Optional<U>
Optional.map(Function<? super T,? extends U> mapper)
default
<U extends Comparable<? super U>>
Comparator<T>
Comparator.thenComparing(Function<? super T,? extends U> keyExtractor)
default <U> Comparator<T>
Comparator.thenComparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)

5. Function.compose(Function before)

Вот определение метода Function.compose:
@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));
    }
    
    // Other default methods ..
}
И мы перепишем этот метод более простым для понимания способом:
@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) -> {
            T t = before.apply(v);
            R r = this.apply(t);
            return r;
        };
    }
    
    // Other default methods ..
}
Например:
FunctionEx6.java
package org.o7planning.ex;

import java.util.function.Function;

public class FunctionEx6 {

    public static void main(String[] args) {
        
        Function<String, Integer> func = content -> content.length();
        
        Function<Article, String> before = article -> article.getContent();
        
        Article article = new Article("Java Tutorial", "Java Tutorial Content...");
        
        int contentLength = func.compose(before).apply(article);
        
        System.out.println("The length of the article content: " + contentLength);
    }
}

class Article {
    private String title;
    private String content;
 
    public Article(String title, String content) {
        this.title = title;
        this.content = content;
    }
    
    public String getTitle() {
        return title;
    }
    public String getContent() {
        return content;
    }
}
Output:
The length of the article content: 24

6. Function.andThen(Function after)

Вот определение метода Function.andThen:
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    // Other default methods ..
}
И мы перепишем этот метод более простым для понимания способом:
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);
    
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        
        return (T t) -> {
            R r = this.apply(t);
            V v = after.apply(r);
            return v;
        };
    }

    // Other default methods ..
}
Например:
FunctionEx7.java
package org.o7planning.ex;

import java.util.function.Function;

public class FunctionEx7 {

    public static void main(String[] args) {
        
        Function<Post, String> func = post -> post.getContent();
        
        Function<String, Integer> after = content -> content.length();
        
        Post post = new Post("Java Tutorial", "Java Tutorial Content...");
        
        int contentLength = func.andThen(after).apply(post);
        
        System.out.println("The length of the post content: " + contentLength);
    }
}

class Post {
    private String title;
    private String content;
 
    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }
    
    public String getTitle() {
        return title;
    }
    public String getContent() {
        return content;
    }
}
Output:
The length of the post content: 24

7. Function.identity()

Статический метод Function.identity(): Возвращает функцию, которая всегда возвращает свой входной аргумент.
static <T> Function<T, T> identity() {
        return t -> t;
}
Например: Преобразует массив в объект Set, содержащий неповторяющиеся элементы.
FunctionEx8.java
package org.o7planning.ex;

import java.util.Arrays;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FunctionEx8 {

    public static void main(String[] args) {

        String[] names = new String[] { //
                "Peter", "Martin", "John", "Peter", //
                "Vijay", "Martin", "Peter", "Arthur" };
        
        Set<String> set = Arrays.asList(names).stream() //
                .map(Function.identity()).collect(Collectors.toSet());
        
        set.forEach(System.out::println);
    }
}
Output:
Vijay
Arthur
John
Martin
Peter
Приведенный выше пример эквивалентен приведенному ниже примеру:
FunctionEx8a.java
package org.o7planning.ex;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

public class FunctionEx8a {

    public static void main(String[] args) {  

        String[] names = new String[] { //
                "Peter", "Martin", "John", "Peter", //
                "Vijay", "Martin", "Peter", "Arthur" };
        
        Set<String> set = Arrays.asList(names).stream() //
                .map(t -> t).collect(Collectors.toSet());
        
        set.forEach(System.out::println);
    }
}

Java Basic

Show More