Java笔记-- 函数式接口和函数式编程

本文介绍了Java中的函数式接口,如Supplier、Consumer、Predicate和Function,重点讲解了Lambda表达式的概念和用途,包括如何提升性能、避免代码浪费,并通过实例展示了Lambda作为参数和返回值的应用。
摘要由CSDN通过智能技术生成

函数式接口

概念

有且仅有⼀个抽象⽅法的接⼝

如:

@FunctionalInterface
public interface Supplier<T> {

    /**
    * Gets a result.
    *
    * @return a result
    */
    T get();
}

格式

也可也自定义一个函数式接口

修饰符 interface 接⼝名称 {
  public abstract 返回值类型 ⽅法名称(可选参数信息);
  // 其他⾮抽象⽅法内容
}

//接口中的抽象方法public abstract可以省略不写,默认为public abstract

函数式编程

概念

在兼顾⾯向对象特性的基础上,Java语⾔通过Lambda表达式与⽅法引⽤等,为开发者打开了函数式编程的⼤⻔。下⾯我们做⼀个初探。

lambda 表达式

为什么要要使用lambda表达式

有些场景的代码执⾏后,结果不⼀定会被使⽤,从⽽造成性能浪费。⽽Lambda表达式是延迟执⾏的,这正好可以作为解决⽅案,提升性能

性能浪费的⽇志案例

注:⽇志可以帮助我们快速的定位问题,记录程序运⾏过程中的情况,以便项⽬的监控和优化。
⼀种典型的场景就是对参数进⾏有条件使⽤,例如对⽇志消息进⾏拼接后,在满⾜条件的情况下
进⾏打印输出:

public class Demo01Logger {
  private static void log(int level, String msg) {
    if (level == 1) {
    System.out.println(msg);
  }
}
  public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";
    log(1, msgA + msgB + msgC);
  }
}

这段代码存在问题:⽆论级别是否满⾜要求,作为 log ⽅法的第⼆个参数,三个字符串⼀定会⾸先被拼接并传⼊⽅法内,然后才会进⾏级别判断。如果级别不符合要求,那么字符串的拼接操作就⽩做了,存在性能浪费。

体验Lambda的更优写法

使⽤Lambda必然需要⼀个函数式接⼝:

@FunctionalInterface
public interface MessageBuilder {
  String buildMessage();
}

然后对log方法进行改造

public class Demo02LoggerLambda {
  private static void log(int level, MessageBuilder builder) {
    if (level == 1) {
      System.out.println(builder.buildMessage());
    }
  }
  public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";
    log(1, () -> msgA + msgB + msgC );
  }
}

这样⼀来,只有当级别满⾜要求的时候,才会进⾏三个字符串的拼接;否则三个字符串将不会进⾏拼接。

使用lambda表达式作为参数和返回值

如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果⽅法的参数是⼀个函数式接⼝类型,那么就可以使⽤Lambda表达式进⾏替代。使⽤Lambda表达式作为⽅法参数,其实就是使⽤函数式接⼝作为⽅法参数。

例如 java.lang.Runnable 接⼝就是⼀个函数式接⼝,假设有⼀个 startThread ⽅法使⽤该接⼝作为参数,那么就可以使⽤Lambda进⾏传参。这种情况其实和 Thread 类的构造⽅法参数为 Runnable 没有本质区别。

public class Demo04Runnable {
  private static void startThread(Runnable task) {
    new Thread(task).start();
  }
  public static void main(String[] args) {
    startThread(() -> System.out.println("线程任务执⾏!"));
  }
}

类似地,如果⼀个⽅法的返回值类型是⼀个函数式接⼝,那么就可以直接返回⼀个Lambda表达式。当需要通过⼀个⽅法来获取⼀个 java.util.Comparator 接⼝类型的对象作为排序器时,就可以调该⽅法获取。

import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
  private static Comparator<String> newComparator() {
    return (a, b) -> b.length() - a.length();
  }
public static void main(String[] args) {
  String[] array = { "abc", "ab", "abcd" };
  System.out.println(Arrays.toString(array));
  Arrays.sort(array, newComparator());
  System.out.println(Arrays.toString(array));
 }
}

其中直接return⼀个Lambda表达式即可。

常⽤函数式接⼝

Supplier接口(生产者)

java.util.function.Supplier 接⼝仅包含⼀个⽆参的⽅法: T get() 。⽤来获取⼀个泛型参数指定类型的对象数据。由于这是⼀个函数式接⼝,这也就意味着对应的Lambda表达式需要“对外提供”⼀个符合泛型类型的对象数据。

import java.util.function.Supplier;
  public class Demo08Supplier {
    private static String getString(Supplier<String> function) {
    return function.get();
  }
  public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    System.out.println(getString(() -> msgA + msgB));
  }
}

当然,更好的写法是使⽤方法引用。

Consumer接⼝(消费者)

抽象方法 accept()

java.util.function.Consumer 接⼝则正好与Supplier接⼝相反,它不是⽣产⼀个数据,⽽是消费⼀个数据,其数据类型由泛型决定。

import java.util.function.Consumer;
  public class Demo09Consumer {
    private static void consumeString(Consumer<String> function) {
      function.accept("Hello");
    }
    public static void main(String[] args) {
      consumeString(s -> System.out.println(s));
    }
 }

默认⽅法 andThen()

andThen()语义是"一步接一步操作"

先去执行调用andThen()方法的调用者的accept()语句,然后执行andThen()里的对象的accept()语句

如果⼀个⽅法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,⾸先做⼀个操作,然后再做⼀个操作,实现组合。⽽这个⽅法就是 Consumer 接⼝中的default⽅法andThen 。

default Consumer<T> andThen(Consumer<? super T> after) {
  Objects.requireNonNull(after);
  return (T t) -> { accept(t); after.accept(t); };
}

先去执行调用andThen()方法的调用者的语句,然后执行andThen()里的对象的语句 如:

import java.util.function.Consumer;
public class Demo10ConsumerAndThen {
  private static void consumeString(Consumer<String> one, Consumer<String> two) {
    one.andThen(two).accept("Hello");
  }
  public static void main(String[] args) {
    consumeString(
      s -> System.out.println(s.toUpperCase()),//one
      s -> System.out.println(s.toLowerCase()));//two
    }
}

Predicate 接口(条件判断)

抽象方法 test()

boolean test(T t)

案例:

import java.util.function.Predicate;
public class Demo15PredicateTest {
  private static void method(Predicate<String> predicate) {
    boolean veryLong = predicate.test("HelloWorld");
    System.out.println("字符串很⻓吗:" + veryLong);
  }

  public static void main(String[] args) {
   method(s -> s.length() > 5);
  }
}

默认方法 and() or() negate()

and()与 案例:

import java.util.function.Predicate;
public class Demo16PredicateAnd {
  private static void method(Predicate<String> one, Predicate<String> two) {
  boolean isValid = one.and(two).test("Helloworld");
  System.out.println("字符串符合要求吗:" + isValid);
}

  public static void main(String[] args) {
   method(s -> s.contains("H"), s -> s.contains("W"));
  }
}

or()或 案例:

import java.util.function.Predicate;
public class Demo16PredicateAnd {
  private static void method(Predicate<String> one, Predicate<String> two) {
    boolean isValid = one.or(two).test("Helloworld");
    System.out.println("字符串符合要求吗:" + isValid);
  }
  
  public static void main(String[] args) {
    method(s -> s.contains("H"), s -> s.contains("W"));
  }
}

negate()非 案例:

import java.util.function.Predicate;
public class Demo17PredicateNegate {
  private static void method(Predicate<String> predicate) {
    boolean veryLong = predicate.negate().test("HelloWorld");
    System.out.println("字符串很⻓吗:" + veryLong);
}

  public static void main(String[] args) {
    method(s -> s.length() < 5);
  }
}

Function接⼝(前者为前置条件,后者为后置条件)

抽象方法 R apply(T t)

根据类型T的参数获取类型R的结果。使⽤的场景例如:将 String 类型转换为 Integer 类型

import java.util.function.Function;
public class Demo11FunctionApply {
  private static void method(Function<String, Integer> function) {
    int num = function.apply("10");
    System.out.println(num + 20);
 }

  public static void main(String[] args) {
   method(s -> Integer.parseInt(s));
  }
}   

默认方法 andThen()

该⽅法同样⽤于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值