2020了你还不会Java8新特性?Lambda表达式及API

Posted wobushitiegan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020了你还不会Java8新特性?Lambda表达式及API相关的知识,希望对你有一定的参考价值。

lambda表达式

为什么要使用lambda表示式

  • 在Java中无法将函数座位参数传递给一个方法,也无法返回一个函数的方法。
  • 在js中,函数的参数是一个函数。返回值是另一个函数的情况是非常常见的。是一门经典的函数式语言。

Java匿名内部类。

匿名内部类的介绍

Gradle的使用。可以完全使用maven的中央仓库。
进行安卓的开发时,gradle已经成为标配了。

lambda:
匿名内部类

 my_jButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button Pressed! ");
            }
        });

改造后

        my_jButton.addActionListener(e -> System.out.println("Button Pressed!"));

lambda表达式的基本结构:

(param1,param2,param3) ->{
    
}

函数式编程: 一个接口里边只有一个抽象方法。
可以通过lambda表达式来实例。

关于函数式接口:

  • 如果一个借口只有一个抽象方法,那么该接口就是一个函数式接口。
  • 如果我们在某一个接口上声明了functionalInterface注解,那么编译器就会按照函数是借口的定义来要求改接口。
  • 如果某个接口只有一个抽象方法,但是我们并没有给接口声明functionnaleInterface注解,编译器依旧会给改接口看作是函数式接口。

通过实例对函数式接口的理解:

package com.erwa.jdk8;

@FunctionalInterface
interface MyInterface {

    void test();

//    Multiple non-overriding abstract methods found in interface com.erwa.jdk8.MyInterface
//    void te();

    //如果一个接口声明一个抽象方法,但是这个方法重写了 object类中的一个方法.
    //接口的抽象方法不会加一.所以依然是函数方法.
    // Object 类是所有类的父类.
    @Override
    String toString();
}

public class Test2 {

    public void myTest(MyInterface myInterface) {
        System.out.println(1);
        myInterface.test();
        System.out.println(2);
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();
        test2.myTest(() -> {
            System.out.println(3);
        });
    }

}

接口里边从1.8开始也可以有方法实现了。default

    默认方法。
   default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
 
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor reference

lambda表达式的作用:

  • lambda表达式为Java添加了确实的函数式编程特性,使我们能将函数当做一等公民看待。
  • 在将函数座位一等公民的语言中,lambda表达式的类型是函数。但是在Java中,lambda表达式是对象,他们必须依附于一类特别的对象类型-函数式接口(function interface)

迭代的方式:

  • 外部迭代:
  • 内部迭代:
  • 方法引用:
list.forEach(System.out::println);

接口中可以有默认方法和静态方法。

流: stream

 /**
     * Returns a sequential {@code Stream} with this collection as its source.
     *
     * <p>This method should be overridden when the {@link #spliterator()}
     * method cannot return a spliterator that is {@code IMMUTABLE},
     * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
     * for details.)
     *
     * @implSpec
     * The default implementation creates a sequential {@code Stream} from the
     * collection's {@code Spliterator}.
     *
     * @return a sequential {@code Stream} over the elements in this collection
     * @since 1.8
     */
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

关于流方式实现的举例:

public static void main(String[] args) {
        //函数式接口的实现方式
        MyInterface1 i1 = () -> {};
        System.out.println(i1.getClass().getInterfaces()[0]);
        MyInterface2 i2 = () -> {};
        System.out.println(i2.getClass().getInterfaces()[0]);

        // 没有上下文对象,一定会报错的.
//        () -> {};

        //通过lambda来实现一个线程.
        new Thread(() -> System.out.println("hello world")).start();

        //有一个list  ,将内容中的首字母变大写输出.
        List<String> list = Arrays.asList("hello","world","hello world");
        //通过lambda来实现所有字母编程大写输出.
//        list.forEach(item -> System.out.println(item.toUpperCase()));
        //把三个单词放入到新的集合里边.
        List<String> list1 = new ArrayList<>();  //diamond语法. 后边的<>不用再放类型
//        list.forEach(item -> list1.add(item.toUpperCase()));
//        list1.forEach(System.out::println);

        //进一步的改进. 流的方式
//        list.stream();//单线程
//        list.parallelStream(); //多线程
        list.stream().map(item -> item.toUpperCase()).forEach(System.out::println);//单线程
        list.stream().map(String::toUpperCase).forEach(System.out::println);

        //上边的两种方法,都满足函数式接口的方式.
    }

lambda表达式的作

  • 传递行为,而不仅仅是值
    • 提升抽象层次
    • API重用性更好
    • 更加灵活

lambda基本语法

  • (argument) -> (body)
  • 如: (arg1,arg2...) -> (body)

Java lambda结构

  • 一个Lambda表达式可以有0个或者多个参数
  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a) 与 (a) 效果相同
  • 所有参数包含在圆括号内,参数之间用逗号相隔。
  • 空圆括号代表参数集为空。
  • 当只有一个参数,且类型可推倒时。圆括号()可省略。
  • lambda表达式的主体可以包含0条或多条语句。
  • 如果lambda表达式的主体只有一条语句,花括号{}可以省略,匿名函数的返回类型与该主体表达式一致。
  • 如果lambda表达式的主体包含一条以上语句,则表达式必须包含在花括号中。匿名函数的韩绘制类型与代码块的返回类型一致,诺没有反回则为空。

高阶函数:
如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数.

传递行为的举例:

  public static void main(String[] args) {
        // 函数的测试
        // 传递行为的一种方式.
        FunctionTest functionTest = new FunctionTest();
        int compute = functionTest.compute(1, value -> 2 * value);

        System.out.println(compute);
        System.out.println(functionTest.compute(2,value -> 5+ value));
        System.out.println(functionTest.compute(3,a -> a * a));

        System.out.println(functionTest.convert(5, a -> a + "hello "));

        /**
         * 高阶函数:
         * 如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数.
         */

    }

    //使用lambda表达式的话,可以直觉预定义行为.用的时候传递.
    // 即 函数式编程.
    public int compute(int a, Function<Integer, Integer> function) {
        return function.apply(a);
    }

    public String convert(int a, Function<Integer, String> function) {
        return function.apply(a);
    }


    // 之前完成行为的做法. 提前把行为定义好,用的时候调用方法. 如:
    public  int method1(int a ){
        return a * 2 ;
    }

Function类中提供的默认方法的讲解:

/**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     返回一个组合的函数。对应用完参数后的结果,再次运行apply
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function  
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

compose : 组合function, 形成两个function的串联。 先执行参数

andThen :先应用当前的函数apply,然后再当做参数再次执行apply。 后执行参数。

identity:输入什么返回什么。

BiFunction: 整合两个函数的方法。
为什么BiFunction不提供 compose ,只提供andThen呢?
因为如果提供compose方法的话,只能获取一个参数的返回值。不合理。

  public static void main(String[] args) {
        FunctionTest2 functionTest2 = new FunctionTest2();

        // compose
//        System.out.println(functionTest2.compute(2,a -> a * 3,b -> b * b));
        // andThen
//        System.out.println(functionTest2.compute2(2,a -> a * 3,b -> b * b));

        //BiFunction
//        System.out.println(functionTest2.compute3(1,2, (a,b) -> a - b));
//        System.out.println(functionTest2.compute3(1,2, (a,b) -> a * b));
//        System.out.println(functionTest2.compute3(1,2, (a,b) -> a + b));
//        System.out.println(functionTest2.compute3(1,2, (a,b) -> a / b));

        //BiFunction  andThen
        System.out.println(functionTest2.compute4(2,3,(a,b) ->a + b , a -> a * a ));
    }

    //compose : 组合function, 形成两个function的串联。  先执行参数
    //andThen :先应用当前的函数apply,然后再当做参数再次执行apply。 后执行参数

    public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.compose(function2).apply(a);
    }

    public int compute2(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
        return function1.andThen(function2).apply(a);
    }

    //BiFunction
    //求两个参数的和
    //先定义一个抽象的行为.
    public int compute3(int a, int b, BiFunction<Integer, Integer, Integer> biFunction) {
        return biFunction.apply(a, b);
    }

    //BiFunction  andThen 
    public int compute4(int a, int b, BiFunction<Integer, Integer, Integer> biFunction, Function<Integer, Integer> function) {
        return biFunction.andThen(function).apply(a, b);
    }

测试 函数式接口的实例:


public class PersonTest {
    public static void main(String[] args) {


        List<Person> personList = new ArrayList<>();

        personList.add(new Person("zhangsan", 20));
        personList.add(new Person("zhangsan", 28));
        personList.add(new Person("lisi", 30));
        personList.add(new Person("wangwu", 40));

        PersonTest test = new PersonTest();

        //测试 getPersonUsername
//        List<Person> personList1 = test.getPersonUsername("zhangsan", personList);
//        personList1.forEach(person -> System.out.println(person.getUsername()));


        //测试  getPersonByAge
        List<Person> personByAge = test.getPersonByAge(25, personList);
        personByAge.forEach(person -> System.out.println(person.getAge()));
        
        
        //测试第三种: 自定义输入行为
        List<Person> list = test.getPersonByAge2(20,personList,(age,persons) ->{
            return persons.stream().filter(person -> person.getAge() > age).collect(Collectors.toList());
        });
        list.forEach(person -> System.out.println(person.getAge()));

    }


    public List<Person> getPersonUsername(String username, List<Person> personList) {
        return personList.stream().filter(person -> person.getUsername().equals(username)).collect(Collectors.toList());
    }

    public List<Person> getPersonByAge(int age, List<Person> personList) {
        //使用BiFunction的方式
//        BiFunction<Integer, List<Person>, List<Person>> biFunction = (ageOfPerson, list) -> {
//            return  list.stream().filter(person -> person.getAge() > ageOfPerson ).collect(Collectors.toList());
//        };

        //变换之后:
        BiFunction<Integer, List<Person>, List<Person>> biFunction = (ageOfPerson, list) ->
            list.stream().filter(person -> person.getAge() > ageOfPerson ).collect(Collectors.toList());

        return biFunction.apply(age, personList);
    }
    
     //第三种方式, 动作也让用户自己定义传进来
    public List<Person> getPersonByAge2(int age ,List<Person> list,BiFunction<Integer,List<Person>,List<Person>> biFunction){
        return biFunction.apply(age, list);
    }
}

函数式接口的真谛: 传递的是行为,而不是数据

  public static void main(String[] args) {
        //给定一个输入参数,判断是否满足条件,满足的话返回true
        Predicate<String> predicate = p -> p.length() > 5;
        System.out.println(predicate.test("nnihaoda"));

    }

到现在为止,只是讲解了Java.lang.function包下的几个最重要的,经常使用的方法。


2020年1月3日08:06:28
BinaryOperator 接口

public class SinaryOpertorTest {

    public static void main(String[] args) {

        SinaryOpertorTest sinaryOpertorTest = new SinaryOpertorTest();

        System.out.println(sinaryOpertorTest.compute(1,2,(a,b) -> a+b));

        System.out.println("-- -- - - - -- -");

        System.out.println(sinaryOpertorTest.getMax("hello123","world",(a,b) -> a.length() - b.length()));
    }

    private int compute(int a, int b, BinaryOperator<Integer> binaryOperator) {
        return binaryOperator.apply(a, b);
    }

    private String getMax(String a, String b, Comparator<String> comparator) {
        return BinaryOperator.maxBy(comparator).apply(a, b);
    }
}

Optional final :Optional 不要试图用来当做参数, 一般只用来接收返回值,来规避值的空指针异常的问题。

  • empty()
  • of()
  • ofNullable()
  • isPresent()
  • get()
  • ...
public class OptionalTest {

    public static void main(String[] args) {
        Optional<String> optional = Optional.of("hello");

        //不确定是否为 空是 调用和这个方法
//        Optional<String> optional2 = Optional.ofNullable("hello");

//        Optional<String> optional1 = Optional.empty();


        //过时
//        if (optional.isPresent()) {
//            System.out.println(optional.get());
//        }

        optional.ifPresent(item -> System.out.println(item));
        System.out.println(optional.orElse("nihao"));
        System.out.println(optional.orElseGet(() -> "nihao"));

    }
public class OptionalTest2 {
    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.setName("dawa");

        Employee employee1 = new Employee();
        employee1.setName("erwa");

        List<Employee> list = Arrays.asList(employee, employee1);
        Company company = new Company("gongsi", list);

        Optional<Company> optionalCompany = Optional.ofNullable(company);

        System.out.println(optionalCompany.map(company1 -> company1.getList()).orElse(Collections.emptyList()));
    }
}

以上是关于2020了你还不会Java8新特性?Lambda表达式及API的主要内容,如果未能解决你的问题,请参考以下文章

2020了你还不会Java8新特性?时间日期API&&Java8总结

2020了你还不会Java8新特性?Collector类源码分析

2020了你还不会Java8新特性?收集器比较器用法详解及源码剖析

Java8新特性,你一定能学会的超详细保姆级源码笔记,看完还不会请直接砍我

Java8新特性,你一定能学会的超详细保姆级源码笔记,看完还不会请直接砍我

Java8新特性,你一定能学会的超详细保姆级源码笔记,看完还不会请直接砍我