Java 8 函数式编程 (1)

本篇对应《Java 8 函数式编程》的第一章和第二章。


什么是函数式编程

面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。

每个人对函数式编程的理解不尽相同。但其核心是:在思考问题时,使用不可变值和函数,函数对一个值进行处理,映射成另一个值。


Lamda 表达式

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

有一点需要注意的是:

Lambda表达式中引用的局部变量必须是final或既成事实上的final变量。


函数式接口

在介绍 Functional Interface 之前,我们先来了解一下另外一个概念(first-class functions)。

In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

这一段话并不难理解,函数是一等公民,一等公民什么都可以做。

  1. 函数可以作为其他函数的参数。
  2. 函数可以作为其他函数的返回值。
  3. 函数可以赋值给变量,可以存储在数据结构中。

为什么要在介绍函数式接口之前先介绍这些呢,因为 函数为第一公民是函数式编程的基础。而 Java 语言的函数显然是不具备这个基础的,所以便有了函数式接口。

只包含一个抽象方法的接口,称为 函数式接口(Functional Interface)。

比如 Consumer 接口,只有一个抽象方法 void accept(T t),参数 T,没有返回值。

package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

我们可以用 Consumer 接口来写的一个 Hello,World。

import java.util.function.Consumer;

public class Main
{
    public static void echo(Consumer<String> consumer, String t) {
        consumer.accept(t);
    }

    public static void main(String[] args) {
        Consumer<String> hello = string -> System.out.println("hello, " + string);

        echo(hello, "world");
    }
}

练习

* Question 1:
* a.
* b. 一元运算符,比如负号,百分比。
* c. x -> x + 1;
*
* Question 2:
* a. N/A
* b. public final static ThreadLocal<DateFormatter> formatter = ThreadLocal.withInitial(() -> new DateFormatter(new SimpleDateFormat(“dd-MMM-yyyy”)));
*
* Question 3:
* a. Yes
* b. Yes
* c. No – the lambda expression could be inferred as IntPred or Predicate<Integer> so the overload is ambiguous.

参考:

https://www.runoob.com/java/java8-lambda-expressions.html

https://www.runoob.com/java/java8-functional-interfaces.html

https://en.wikipedia.org/wiki/First-class_function

https://kotlinlang.org/docs/reference/lambdas.html

https://www.ibm.com/developerworks/cn/java/j-understanding-functional-programming-3/

225 total views, no views today