java8函数式编程

原文链接http://zhhll.icu/2020/05/11/java%E5%9F%BA%E7%A1%80/java8/java%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/

函数式编程

函数式编程强加了额外的约束,即所有数据必须是不可变的:设置一次,永不改变。将值传递给函数,该函数然后生成新值但从不修改自身外部的任何东西,不可变对象和无副作用范式解决了并发编程中最基本和最棘手的问题之一。

Lambda表达式只支持函数式接口,也就是只有一个抽象方法的接口

普通用法和函数式编程对比

@FunctionalInterface //用于判断是否符合函数式接口
public interface Interf {
    String f(String msg);
}

public class InterfImpl implements Interf {
    @Override
    public String f(String msg) {
        return msg + " 普通实现";
    }

    static String func(String msg) {
        return msg + " 方法引用";
    }

    public static void main(String[] args) {
        Interf[] interfs = new Interf[]{
                new InterfImpl(),//普通实现
                new Interf() {
                    @Override
                    public String f(String msg) {
                        return msg + " 匿名内部类";
                    }
                },//匿名内部类
                msg -> msg + " lambda", //lambda表达式
                InterfImpl::func //方法引用
        };

        for(Interf interf : interfs){
            System.out.println(interf.f("测试lambda"));
        }
    }
}

lambda表达式

lambda表达式,由箭头->分隔开参数和函数体,箭头左边是参数,右边是lambda返回的表达式,即函数体

lambda表达式就是函数式接口的实例

  • 当只有一个参数,可以不用括号()
  • 如果没有参数,必须使用()表示空参数列表
  • 对于多个参数,将参数列表放在括号()中
  • 如果有多行,需要将这些行放在花括号,在这种情况下,需要使用return

四个内置函数式接口

Consumer 消费型接口

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
public static void testConsumer(){
  Consumer<Double> consumer = d -> System.out.println("花费"+d+"元");
  consumer.accept(1000d);
}

Supplier 供给型接口

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
public static void testSupplier(){
  Supplier<Integer> supplier = () -> {
    return 100;
  };
  System.out.println(supplier.get());
}

Function 函数型接口

@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;
    }
}
public static void testFunction(){
  Function<Integer,Integer> function = x -> {
    return x*100;
  };
  System.out.println(function.apply(10));
}

Predicate 断言型接口

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
public static void testPredicate(){
  Predicate<String> predicate = x -> {
    return "yes".equals(x);
  };
  System.out.println(predicate.test("no"));
}

递归

递归的Lambda表达式,递归方法必须是实例变量或静态变量

计算阶乘

@FunctionalInterface
public interface IntCall {
    int call(int i);
}

public class Recursion {
    private IntCall intCall;
    public static void main(String[] args) {
        Recursion recursion = new Recursion();
        recursion.intCall = n -> n == 0 ? 1 : n * recursion.intCall.call(n - 1);
        for (int i = 0; i < 10; i++)
            System.out.println(i+"! = "+recursion.intCall.call(i));
    }
}

方法引用

java8的方法引用,由::区分,在::左边是类或对象的名称,在::的右边是方法的名称,但是没有参数列表

如果lambda体的内容有方法已经实现了,可以使用方法引用

方法引用其实也是lambda表达式,要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同

三种语法格式
1、对象::实例方法
2、类::静态方法
3、类::实例方法 当lambda表达式第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用

public class TestMethodRef {

    /**
     * 对象::实例方法
     */
    public static void test1(){
        Consumer<String> con =  System.out::println;
        con.accept("aaaa");

    }

    /**
     * 类::静态方法
     */
    public static void test2(){
        Consumer<String> con =  TestMethodRef::testStatic;
        con.accept("xxxx");
    }

    /**
     * 类::实例方法
     */
    public static void test3(){
        MyTest myTest = String::equals;
    }

    /**
     * 测试无参构造器
     * @return
     */
    public static TestMethodRef test4(){
        Supplier<TestMethodRef> supplier = TestMethodRef::new;
        return supplier.get();
    }

    /**
     * 测试有参构造器
     * @return
     */
    public static TestMethodRef test5(){
        Function<Integer,TestMethodRef> function = TestMethodRef::new;
        return function.apply(10);
    }

    public TestMethodRef(){
        System.out.println("无参构造器调用");
    }

    private int x;
    public TestMethodRef(int x){
        this.x = x;
    }



    public static void main(String[] args) {
        test1();
        test2();
        test3();
        test4();
        TestMethodRef t = test5();
        System.out.println(t.x);
    }

    public static void testStatic(String x){
        System.out.println("我输出入了"+x);
    }


}

@FunctionalInterface
interface MyTest{
    boolean test(String x,String y);
}

由于本身的博客百度没有收录,博客地址http://zhhll.icu

posted @ 2021-01-15 23:37  拾光师  阅读(152)  评论(0编辑  收藏  举报