[书]java8函数式编程(1)

一:Learning

package testLambda;

import java.awt.Button;
import java.awt.event.ActionEvent;
import java.util.EventListener;
import java.util.function.BinaryOperator;
import org.junit.Test;

/**
 * @author zhangdi
 * @description Lambda
 */
public class LambdaChapter1and2 {
    public static void main(String[] args) {

    }

    /**
     * 2.1 辨别Lambda表达式
     */
    @SuppressWarnings("unused")
    @Test
    public static void recognizeLambda() {
        // 1.不包含参数,使用()表示,没有参数,该Lamdba表达式实现了Runnable接口,该接口也只有一个run方法,没有参数,返回类型为void
        Runnable NoArguments = () -> System.out.println("hello world");
        // 2.该Lamdba表达式包含且只包含一个参数可省略参数的括号
        ActionListener oneArgument = (event) -> System.out.println("button clicked");
        ActionListener oneArgument2 = event -> System.out.println("button clicked");
        // 3.可以使用{}将lamdba表达式的主体括起来;只有一行代码的lambda的表达式也可以使用大括号;用来明确表达式的开始与结束
        Runnable multistatement = () -> {
            System.out.println("hello");
            System.out.println("world");
        };
        // 4.lambda表达式也可以是包含多个参数的方法;
        BinaryOperator<Long> add = (x, y) -> x + y;
        BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

    }

    /**
     * 2.2 函数接口是只有一个抽象方法的接口,用作lambda表达式的类型
     */
    public static void 函数接口() {

    }

    /**
     * 2.3 引用值,而不是变量
     * 既成事实上的 final 是指只能给该变量赋值一次。 换句话说, Lambda 表达式引用的是值,而不是变量
     * Lambda 表达式中引用既成事实上的 final 变量  -->Lambda 表达式都是静态类型
     */
    public static void valueReference() {
        Button button = new Button();
        String name = getUserName();
        button.addActionListener(event -> System.out.println("hi " + name));
    }
    private static String getUserName() {
        // TODO Auto-generated method stub
        return "test";
    }


    /**
     * 2.4 ActionListener 接口: 接受 ActionEvent 类型的参数, 返回空
     * 
     *         ActionListener 只有一个抽象方法: actionPerformed, 被用来表示行为: 接受一个参数, 返回空。
     *         记住, 由于 actionPerformed 定义在一个接口里, 因此 abstract 关键字不是必需 的。
     *         该接口也继承自一个不具有任何方法的父接口: EventListener。
     */
    public interface ActionListener extends EventListener {
        public void actionPerformed(ActionEvent event);
    }

    /**
     *java中重要的函数接口
     * 接口                   参数          返回类型        示例
     * Predicate<T>         T       boolean     这张唱片已经发行了吗
     * Consumer<T>          T       void        输出一个值
     * Function<T,R>        T       R           获得 Artist 对象的名字
     * Supplier<T>          None    T           工厂方法 
     * UnaryOperator<T>     T       T           逻辑非( !)
     * BinaryOperator<T>    (T, T)  T           求两个数的乘积( *)
     * 
     *  2.5类型推断
     *  Predicate 用来判断真假的函数接口
     */
    public static void 类型推断() {
        Predicate<Integer> atLeast5 = x -> x > 5;

    }
    //Predicate 接口的源码, 接受一个对象, 返回一个布尔值
    public interface Predicate<T> {
        boolean test(T t);
    }
    //略显复杂的类型推断 :类型推断系统相当智能, 但若信息不够, 类型推断系统也无能为力。 类型系统不会漫无边
    //际地瞎猜, 而会中止操作并报告编译错误, 寻求帮助 ,如去掉Long,代码不会通过编译
    BinaryOperator<Long> addLongs = (x, y) -> x + y;
    //没有泛型, 代码则通不过编译




}


java中重要的函数接口:
这里写图片描述
总结:
* Lambda 表达式是一个匿名方法, 将行为像数据一样进行传递。
* Lambda 表达式的常见结构: BinaryOperator add = (x, y) → x + y。
* Lambda表达式里引用的到的变量是final的,本质是值,不是变量。
* Lambda表达式的类型是个函数接口,即仅有一个抽象方法的接口。( 函数接口指仅具有单个抽象方法的接口, 用来表示 Lambda 表达式的类型。)

二: 练习
1. 请看例 2-15 中的 Function 函数接口并回答下列问题。
例 2-15 Function 函数接口

    public interface Function<T, R> {
        R apply(T t);
    }
a. 请画出该函数接口的图示。
b. 若要编写一个计算器程序, 你会使用该接口表示什么样的 Lambda 表达式?
c. 下列哪些 Lambda 表达式有效实现了 Function<Long,Long> ?
    x -> x + 1;
    (x, y) -> x + 1;
    x -> x == 1;
2. ThreadLocal Lambda 表达式。 Java 有一个 ThreadLocal 类, 作为容器保存了当前线程里
局部变量的值。 Java 8 为该类新加了一个工厂方法, 接受一个 Lambda 表达式, 并产生
一个新的 ThreadLocal 对象, 而不用使用继承, 语法上更加简洁。
a. 在 Javadoc 或集成开发环境( IDE) 里找出该方法。
b. DateFormatter 类是非线程安全的。 使用构造函数创建一个线程安全的 DateFormatter
对象, 并输出日期, 如“ 01-Jan-1970”。

3. 类型推断规则。 下面是将 Lambda 表达式作为参数传递给函数的一些例子。 javac 能正
确推断出 Lambda 表达式中参数的类型吗? 换句话说, 程序能编译吗?
a. Runnable helloWorld = () -> System.out.println("hello world");
b. 使用 Lambda 表达式实现 ActionListener 接口:
    JButton button = new JButton();
    button.addActionListener(event ->
    System.out.println(event.getActionCommand()));
c. 以如下方式重载 check 方法后, 还能正确推断出 check(x -> x > 5) 的类型吗?
    interface IntPred {
    boolean test(Integer value); 
    }
    boolean check(Predicate<Integer> predicate);
    boolean check(IntPred predicate);

练习答案:


public class Chapter1And2_practice {

    public static class Question1 {
        //x -> x + 1;
    }

    public static class Question3 {
        //a.yes
        //b.yes
        //c.no
    }

    /**
     * @author NSNP736
     * @description Question2
     */
    public static class Question2 {
        //lamada-by richard
        public final static ThreadLocal<DateFormatter> formatter = ThreadLocal.withInitial(() -> new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy")));
        //Anonymous Inner Class -byzhangdi
        public final static ThreadLocal<DateFormatter> formatter2 = ThreadLocal.withInitial( new Supplier<DateFormatter>(){

            @Override
            public DateFormatter get() {
                DateFormatter dateFormatter = new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy"));
                return dateFormatter;
            }});
    }
    @Test
    public void exampleInB() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 1970);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        //formatter
        String format1 = Question2.formatter.get().getFormat().format(cal.getTime());
        //formatter2
        ThreadLocal<DateFormatter> formatter = Question2.formatter;
        DateFormatter dateFormatter = formatter.get();
        Format format = dateFormatter.getFormat();
        String format2 = format.format(cal.getTime());

        assertEquals("01-一月-1970", format1);
        assertEquals("01-一月-1970", format2);
    }
}
原文地址:https://www.cnblogs.com/DiZhang/p/12545014.html