Lambda表达式

函数式编程思想概述

在数学中,函数就是有输入量,输出量的一套计算方案,也就是拿数据做操作,面向对象思想强调,必须通过对象的形式来做事情,函数式思想则尽量忽略面向对象的复杂语法:强调做什么,而不是以什么形式去做,而Lambda表达式就是函数式思想的体现

体验Lambda表达式

需求:启动一个线程,在控制台输出一句话

方式1:

  • 定义一个类MyRunnable实现Runnable接口,重写run()方法
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
  • 启动线程

方式2:

  • 匿名内部类的方式改进

方式3:

  • Lambda表达式的方式改进

MyRunnable

public class MyRunnable implements Runnable {

  @Override
  public void run() {
    System.out.println("Here we go!!!");
  }
}

LambdaDemo

  public static void main(String[] args) {
    //实现类的方式
    MyRunnable runnable = new MyRunnable();
    Thread t = new Thread(runnable);
    t.start();
    //使用匿名内部类的方式改进
    new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println("I don't want to go");
      }
    }).start();
    //Lambda表达式的方式
    new Thread(() -> {
      System.out.println("This is Lambda");
    }).start();
  }

Lambda表达式的标准格式

匿名内部类中重写run()方法的代码分析

  • 方法形式参数为空,说明调用方法时不需要传递参数
  • 方法返回值类型为void,说明方法执行没有结果返回
  • 方法体中的内容,是我们具体要做的事情

Lambda表达式的代码分析

  • ()里面没有内容,可以看成是方法形式参数为空
  • ->用箭头指向后面要做的事情
  • {}包含一段代码,我们称之为代码块,可以看成是方法体中的内容

组成Lambda表达式的三要素:形式参数,箭头,代码块

Lambda表达式的格式

  • 格式:(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可
  • ->:由英文中画线和大宇符号组成,固定写法,代表指向动作
  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

Lambda表达式的练习

Lambda表达式的使用前提

  • 有一个接口
  • 接口中有且仅有一个抽象方法

练习1

  • 定义一个接口(Eatable),里面定义一个抽象方法:void eat()
  • 定义一个测试类(EatableDemo),在测试类中提供两个方法

    • 一个方法是:useEatable(Eatable e)
    • 一个方法是主方法,在主方法中调用useEatable方法

Eatable

public interface Eatable {

  void eat();
}

EatableImpl

public class EatableImpl implements Eatable {

  @Override
  public void eat() {
    System.out.println("I can eat,i can't breath");
  }
}

EatableDemo

  public static void main(String[] args) {
    //在主方法中调用useEatable
    Eatable e = new EatableImpl();
    useEatable(e);
    //匿名内部类
    useEatable(new Eatable() {
      @Override
      public void eat() {
        System.out.println("I can't eat,i can't breath");
      }
    });
    //Lambda表达式
    useEatable(() -> {
      System.out.println("I can't eat,but i can breath");
    });
  }

  private static void useEatable(Eatable e) {
    e.eat();
  }

练习2

  • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s)
  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法

    • 一个方法是:useFlyable(Flyable f)
    • 一个方法是主方法,在主方法中调用useFlyable方法

Flyable

public interface Flyable {

  void fly(String s);
}

FlyableDemo

  public static void main(String[] args) {
    //主方法中调用useFlyable
    //匿名内部类
    useFlyable(new Flyable() {
      @Override
      public void fly(String s) {
        System.out.println(s);
        System.out.println("dog can't fly,but can run");
      }
    });
    System.out.println("---------");
    //Lambda
    useFlyable((String s) -> {
      System.out.println(s);
      System.out.println("cat can't fly too");
    });
  }

  private static void useFlyable(Flyable f) {
    f.fly("birds can fly");
  }

练习3

  • 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y)
  • 定义一个测试类(AddableDemo),在测试类中提供两个方法

    • 一个方法是:useAddable(Addable a)
    • 一个方法是主方法,在主方法中调用useAddable方法

Addable

public interface Addable {

  int add(int x, int y);
}

AddableDemo

  public static void main(String[] args) {
    //匿名内部类
    useAddable(new Addable() {
      @Override
      public int add(int x, int y) {
        return x + y;
      }
    });
    //Lambda
    useAddable((int a, int b) -> {
      //return a + b;
      //所以要看它的实现方法是怎么样的,举例这里虽然是a,b,但我如果相减,返回的3-4=-1,相当于add方法是相减的
      return a - b;
    });
  }

  private static void useAddable(Addable a) {
    int sum = a.add(3, 4);
    System.out.println(sum);
  }

Lambda表达式的省略模式

省略规则

  • 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号和分号,如果有return,甚至是return

LambdaDemo2

  public static void main(String[] args) {
    //参数的类型可以省略
    useAddable((x, y) -> {
      return x + y;
    });
    //如果参数有且仅有一个,那么小括号也可以省略
    useFlyable(s -> {
      System.out.println(s);
    });
    //如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略
    useFlyable(s -> System.out.println(s));
    useAddable((x, y) -> x + y);
  }

  private static void useFlyable(Flyable f) {
    f.fly("dog can't fly");
  }

  private static void useAddable(Addable a) {
    int sum = a.add(3, 4);
    System.out.println(sum);
  }

Lambda表达式的注意事项

注意事项

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
  • 必须有上下文环境,才能推导出Lambda对应的接口

    • 根据局部变量的赋值得知Lambda对应的接口:Runnable r = () -> System.out.println("Lambda表达式");
    • 根据调用方法的参数得知Lambda对应的接口:new Thread(() -> System.out.println("Lambda表达式")).start();

LambdaDemo3

      public static void main(String[] args) {
    //使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
    useInter(() ->
        System.out.println("I like fruits")
    );
    //必须要有上下文环境,才能推导出Lambda对应的接口
    new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println("匿名内部类");
      }
    }).start();
    Runnable r = () -> System.out.println("Lambda表达式");
    new Thread(r).start();
    new Thread(() -> System.out.println("Lambda表达式")).start();
  }

  private static void useInter(Inter i) {
    i.show();
  }

Lambda表达式和匿名内部类的区别

所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
  • Lambda表达式:只能是接口

使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同

  • 匿名内部类:编译后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行的时候动态生成

LambdaDemo4

  public static void main(String[] args) {
    //匿名内部类
    useInter(new Inter() {
      @Override
      public void show() {
        System.out.println("接口");
      }
    });
    useAnimal(new Animal() {
      @Override
      public void method() {
        System.out.println("抽象类");
      }
    });
    useStudent(new Student() {
      @Override
      public void study() {
        System.out.println("具体类");
      }
    });
    System.out.println("--------");
    //Lambda
    useInter(() -> System.out.println("接口"));
  }

  private static void useStudent(Student s) {
    s.study();
  }

  private static void useAnimal(Animal a) {
    a.method();
  }

  private static void useInter(Inter i) {
    i.show();
  }

Inter

public interface Inter {

  void show();
}

Animal

public abstract class Animal {

  public abstract void method();
}

Student

public class Student {

  public void study() {
    System.out.println("I love study");
  }
}

接口组成更新

接口组成更新描述

接口的组成

  • 常量public static final
  • 抽象方法public abstract
  • 默认方法Java 8
  • 静态方法java 8
  • 似有方法java 9

接口中默认方法

接口中默认方法的定义

  • 格式:public default 返回值类型 方法名(参数列表){}
  • 例:public default void show3(){}

接口中默认方法的注意事项

  • 默认方法不是抽象方法,所以不强制被重写,但是可以被重写,重写的时候去掉default关键字
  • public可以省略,default不能被省略

MyInterface

public interface MyInterface {

  void show1();

  void show2();

  //Java8的默认方法
  public default void show3() {
    System.out.println("show3");
  }
}

MyInterfaceImplOne

public class MyInterfaceImplOne implements MyInterface {

  @Override
  public void show1() {
    System.out.println("One show1");
  }

  @Override
  public void show2() {
    System.out.println("One show2");
  }

  @Override
  public void show3() {
    System.out.println("One show3");
  }
}

MyInterfaceImpTwo

public class MyInterfaceImpTwo implements MyInterface {

  @Override
  public void show1() {
    System.out.println("Two show1");
  }

  @Override
  public void show2() {
    System.out.println("Two show2");
  }
}

MyInterfaceDemo

  public static void main(String[] args) {
    MyInterface my1 = new MyInterfaceImplOne();
    MyInterface my2 = new MyInterfaceImpTwo();
    my1.show1();
    my1.show2();
    my1.show3();
    my2.show3();
  }

接口中静态方法

接口中静态方法的定义格式

  • 格式:public static 返回值类型 方法名(参数列表){}
  • 范例:public static void show(){}

接口中静态方法的注意事项

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略

Inter

public interface Inter {

  void show();

  default void method() {
    System.out.println("Inter's default");
  }

  public static void test() {
    System.out.println("Inter's static");
  }
}

implements

public class InterImpl implements Inter {

  @Override
  public void show() {
    System.out.println("Inter's show");
  }
}

InterDemo

  public static void main(String[] args) {
    Inter i = new InterImpl();
    i.show();
    i.method();
    Inter.test();
  }

接口中私有方法

Java9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔,Java8允许在接口中定义带方法体的默认方法和静态方法,这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java9增加私有方法的必然性

接口中私有方法的定义格式

  • 格式1:private 返回值类型 方法名(参数列表){}
  • 范例1:private void show(){}
  • 格式2:private static 返回值类型 方法名(参数列表){}
  • 范例2:private static void method(){}

接口中私有方法的注意事项

  • 默认方法可以调用私有的静态方法和非静态方法
  • 静态方法只能调用私有的静态方法

方法引用

体验方法引用

在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作,那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要,那我们又是如何使用已经存在的方案的呢?

Printable

public interface Printable {

  void printString(String s);
}

PrintableDemo

  public static void main(String[] args) {
    usePrintable((String s) -> {
      System.out.println(s);
    });
    //方法引用符号: ::
    usePrintable(System.out::println);
  }

  private static void usePrintable(Printable p) {
    p.printString("I'm so tired");
  }

方法引用符

方法引用符

  • ::该符号为引用符号,而它所在的表达式被称为方法引用

回顾一下我们在体验方法引用的代码

  • Lambda表达式:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
  • 方法引用:直接使用System.out中的println方法来取代Lambda,代码更加的简洁

推导与省略

  • 如果使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导
  • 如果使用方法引用,也是同样可以根据上下文进行推导
  • 方法引用是Lambda的孪生兄弟

Lambda表达式支持的方法引用

常见的引用方法

  • 引用类方法
  • 引用对象的实例方法
  • 引用类的实例方法
  • 引用构造器

引用类方法

引用类方法,其实就是引用类的静态方法

  • 格式:类名::静态方法
  • 范例:Integer::parseInt,Integer类的方法:public static parseInt(String s)将此String转换为int类型数据

练习:

  • 定义一个接口(Converter),里面定义一个抽象方法:int convert(String s);
  • 定义一个测试类(ConverterDemo),在测试类中提供两个方法,一个方法是:useConverter(Converter c),一个方法是主方法,在主方法中调用useConverter方法

Converter

public interface Converter {

  int convert(String s);
}

ConverterDemo

  public static void main(String[] args) {
    //使用Lambda表达式
    useConverter(s -> Integer.parseInt(s));
    //引用类方法
    useConverter(Integer::parseInt);
    //Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
  }

  private static void useConverter(Converter c) {
    int i = c.convert("6324");
    System.out.println(i);
  }

引用对象的实例方法

引用对象的实例方法,其实就引用类中的成员方法

  • 格式:对象::成员方法
  • 范例:HellowWorld::toUpperCase,String类中的方法:public String toUpperCase()将此String所有字符转换为大写

练习

  • 定义一个类(PrintString),里面定义一个方法:public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
  • 定义一个接口(Printer),里面定义一个抽象方法:void printUpperCase(String s)
  • 定义一个测试类(PrinterDemo),在测试类中提供两个方法,一个方法是:usePrinter(Printer p),一个方法是主方法,在主方法中调用usePrinter方法

PrintString

public class PrintString {

  public void printUpper(String s) {
    String result = s.toUpperCase();
    System.out.println(result);
  }
}

Printer

public interface Printer {

  void printUpperCase(String s);
}

PrinterDemo

  public static void main(String[] args) {
    usePrinter((String s) -> {
      String result = s.toUpperCase();
      System.out.println(result);
    });
    //引用对象的实例方法
    PrintString ps = new PrintString();
    usePrinter(ps::printUpper);
    //Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
  }

  private static void usePrinter(Printer p) {
    p.printUpperCase("HelloWorld");
  }

引用类的实例方法

引用类的实例方法,其实就是引用类中的成员方法

  • 格式:类名::成员方法
  • 范例:String::substring,String类中的方法:public String substring(int beginIndex,int endIndex)从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为endIndex-beginIndex

练习

  • 定义一个接口(MyString),里面定义一个抽象方法:String mySubString(String s,int x,int y)
  • 定义一个测试类(MyStringDemo),在测试类中提供两个方法,一个方法是:useMyString(MyString my),一个方法是主方法,在主方法中调用useMyString方法

MyString

public interface MyString {

  String mySubString(String s, int x, int y);
}

MyStringDemo

  public static void main(String[] args) {
    useMyString((String s, int x, int y) -> {
      return s.substring(x, y);
    });
    //引用类中的实例方法
    useMyString(String::substring);
    //Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
  }

  private static void useMyString(MyString my) {
    String s = my.mySubString("Where are you now", 3, 12);
    System.out.println(s);
  }

引用构造器

引用构造器,其实就是引用构造方法

  • 格式:类名::new
  • 范例:Student::new

练习

  • 定义一个类(Student),里面有两个成员变量(name,age),并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
  • 定义一个接口(StudentBuilder),里面定义一个抽象方法Student build(String name,int age)
  • 定义一个测试类(StudentDemo),在测试类中提供两个方法,一个方法是:useStudentBuilder(StudentBuilder s),一个方法是主方法,在主方法中调用useStudentBuilder方法

Student

public class Student {

  private String name;
  private int age;

  public Student() {
  }

  public Student(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
}

StudentBuilder

public interface StudentBuilder {

  Student build(String name, int age);
}

StudentDemo

  public static void main(String[] args) {
    useStudentBuilder((String name, int age) -> {
      return new Student(name, age);
    });
    //引用构造器
    useStudentBuilder(Student::new);
    //Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
  }

  private static void useStudentBuilder(StudentBuilder sb) {
    Student s = sb.build("YuKi", 21);
    System.out.println(s.getName() + " " + s.getAge());
  }

函数式接口

函数式接口概述

函数式接口:有且仅有一个抽象方法的接口

Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口,只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导

如何检测一个接口是不是函数式接口?

  • @FunctionalInterface
  • 放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败

注意

  • 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口,但是建议加上该注解

函数式接口作为方法的参数

需求

  • 定义一个类(RunnableDemo),在类中提供两个方法,一个方法是:startThread(Runnable r),方法参数Runnable是一个函数式接口,一个方法是主方法,在主方法中调用startThread方法

如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递

RunnableDemo

  public static void main(String[] args) {
    //匿名内部类的方式
    startThread(new Runnable() {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getName() + "线程启动了");
      }
    });
    //Lambda表达式
    startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
  }

  private static void startThread(Runnable r) {
    new Thread(r).start();
  }

函数式接口作为方法的返回值

需求

  • 定义一个类(ComparatorDemo),在类中提供两个方法,一个方法是:Comparator<String> getComparator(),方法返回值Comparator是一个函数式接口,一个方法是主方法,在主方法中调用getComparator方法

如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回

ComparatorDemo

  public static void main(String[] args) {
    //构造使用场景
    //定义集合,存储字符串元素
    ArrayList<String> array = new ArrayList<>();
    array.add("YuKi");
    array.add("XP");
    array.add("XianKe");
    array.add("LaoWang");
    System.out.println("排序前" + array);
    Collections.sort(array, getComparator());
    System.out.println("排序后" + array);
  }

  private static Comparator<String> getComparator() {
    //匿名内部类的方式实现
//    return new Comparator<String>() {
//      @Override
//      public int compare(String o1, String o2) {
//        return o1.length() - o2.length();
//      }
//    };
    //Lambda表达式
    return (s1, s2) -> s1.length() - s2.length();
  }

常见的函数式接口

Java8在java.util.function包下预定义了大量的函数式接口供我们使用

重点了解这几个

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

Supplier接口

Supplier<T>:包含一个无参的方法

  • T get():获得结果
  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier <T>:接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用

SupplierDemo

  public static void main(String[] args) {
    String s = getString(() -> {
      return "YuKi";
    });
    System.out.println(s);
    Integer i = getInteger(() -> 6324);
    System.out.println(i);
  }

  //定义一个方法,返回一个整数数据
  private static Integer getInteger(Supplier<Integer> sup) {
    return sup.get();
  }

  //定义一个方法,返回一个字符串数据
  private static String getString(Supplier<String> sup) {
    return sup.get();
  }

练习

  • 定义一个类(SupplierTest),在类中提供两个方法,一个方法是:int getMax(Supplier <Integer>) sup,用于返回一个int数组中的最大时,一个方法是主方法,在主方法中调用getMax方法

SupplierTest

  public static void main(String[] args) {
    //定义一个数组
    int[] arr = {23, 12, -2, 134, 289};
    int result = getMax(() -> {
          int max = arr[0];
          for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
              max = arr[i];
            }
          }
          return max;
        }
    );
    System.out.println(result);
  }

  /**
   * 返回一个int数组中的最大值
   */
  private static int getMax(Supplier<Integer> sup) {
    return sup.get();
  }

Consumer接口

Consumer<T>:包含两个方法

  • void accept(T t):对给定的参数执行此操作
  • default Consumer<T> andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
  • Consumer<T>接口也被称为消费型接口,它消费的数据的数据类型由泛型指定

ConsumerDemo

  public static void main(String[] args) {
    operatorString("YuKi", (String s) -> {
      System.out.println(s);
    });
    operatorString("YuKi", System.out::println);
    operatorString("XP", s -> {
      System.out.println(new StringBuilder(s).reverse().toString());
    });
    System.out.println("-----------");
    operatorString("YuKi", s -> System.out.println(s),
        s -> System.out.println(new StringBuilder(s).reverse().toString()));
  }

  /**
   * 定义一个方法,消费一个字符串数据
   */
  private static void operatorString(String name, Consumer<String> con) {
    con.accept(name);
  }

  /**
   * 定义一个方法,用不同的方式消费同一个字符串两次
   */
  private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
    //这里可以无限的跟 a.andThen(b).andThen(c).andThen(d).accept
    con1.andThen(con2).accept(name);
  }

练习

  • String[] strArray={"YuKi,21","XP,21","XianKe,22"}
  • 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
  • 要求:把打印姓名的动作作为第一个Consumer接口的Lambda实例,把打印年龄的动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序组合到一起使用

ConsumerTest

  public static void main(String[] args) {
    String[] strArray = {"YuKi,21", "XP,21", "XianKe,22"};
    printInfo(strArray, (String s) -> {
      String name = s.split(",")[0];
      System.out.print("姓名:" + name + ",");
    }, (String s) -> {
      String age = s.split(",")[1];
      System.out.println("年龄:" + age);
    });
  }

  private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
    for (String s : strArray) {
      con1.andThen(con2).accept(s);
    }
  }

Predicate接口

Predicate<T>:常用的四个方法

  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
  • default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
  • default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与
  • default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或
  • Predicate<T>接口通常用于判断参数是否满足指定的条件

PredicateDemo1

  public static void main(String[] args) {
    boolean b1 = checkString("YuKi", (String s) -> {
      return s.length() > 5;
    });
    System.out.println(b1);
    boolean b2 = checkString("Hello world", s -> s.length() > 5);
    System.out.println(b2);
    boolean b3 = isString("Hello world", s -> s.length() > 5);
    System.out.println(b3);
  }

  /**
   * 判断给定的字符串是否满足要求
   */
  private static boolean checkString(String s, Predicate<String> pre) {
    return pre.test(s);
  }

  private static boolean isString(String s, Predicate<String> pre) {
    return pre.negate().test(s);
  }

PredicateDemo2

  public static void main(String[] args) {
    boolean b1 = checkString("YuKi", s -> s.length() > 5, s -> s.length() < 10);
    System.out.println(b1);
    boolean b2 = orString("YuKi", s -> s.length() < 5, s -> s.length() > 15);
    System.out.println(b2);
  }

  /**
   * 同一个字符串给出两个不同的判断条件,最后把这两个判断的结果做逻辑与运算的结果作为最终的结果
   */
  private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
    return pre1.and(pre2).test(s);
  }

  private static boolean orString(String s, Predicate<String> pre1, Predicate<String> pre2) {
    return pre1.or(pre2).test(s);
  }

练习

  • String[] strArray={"YuKi,21","XP,21","LaoWang,21","XianKe,22"}
  • 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
  • 同时满足如下要求:姓名长度大于2,年龄大于21
  • 分析:有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断,必须同时满足两个条件,所以可以使用and方法连接两个判断条件

PredicateTest

  public static void main(String[] args) {
    String[] strArray = {"YuKi,21", "XP,21", "LaoWang,21", "XianKe,22"};
    ArrayList<String> person = myFilter(strArray, s -> s.split(",")[0].length() > 2,
        s -> Integer.parseInt(s.split(",")[1]) > 21
    );
    for (String s : person) {
      System.out.println(s);
    }
  }

  /**
   * 通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
   */
  private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1,
      Predicate<String> pre2) {
    //定义一个集合
    ArrayList<String> array = new ArrayList<>();
    for (String s : strArray) {
      if (pre1.and(pre2).test(s)) {
        array.add(s);
      }
    }
    return array;
  }

Function接口

Function<T,R>:常用的两个方法

  • R apply(T t):将此函数应用于给定的参数
  • default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
  • Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值

FunctionDemo

  public static void main(String[] args) {
    convert("32", s -> Integer.parseInt(s));
    convert(20, i -> String.valueOf(i + 3));
    convert("62", s -> Integer.parseInt(s) + 3, integer -> String.valueOf(integer));
  }

  /**
   * 定义一个方法,把一个字符串转化int类型,在控制台输出
   */
  private static void convert(String s, Function<String, Integer> fun) {
    Integer i = fun.apply(s);
    System.out.println(i);
  }

  /**
   * 定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
   */
  private static void convert(Integer i, Function<Integer, String> fun) {
    String s = fun.apply(i);
    System.out.println(s);
  }

  /**
   * 定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,再转为字符串在控制台输出
   */
  private static void convert(String s, Function<String, Integer> fun1,
      Function<Integer, String> fun2) {
    String str = fun1.andThen(fun2).apply(s);
    System.out.println(str);
  }

练习

  • String s="YuKi,21"
  • 请按照要求进行操作

    1. 将字符串截取到数字年龄部分
    2. 将上一步的年龄字符串转换成int类型的数据
    3. 将上一步的int数据加50,得到一个int结果,在控制台输出
  • 请通过Function接口来实现函数拼接

FunctionTest

  public static void main(String[] args) {
    String s = "YuKi,21";
    convert(s, s1 -> s1.split(",")[1], s1 -> Integer.parseInt(s1), i -> i + 50);
  }

  private static void convert(String s, Function<String, String> fun1,
      Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
    Integer ss = fun1.andThen(fun2).andThen(fun3).apply(s);
    System.out.println(ss);
  }
Last modification:May 17, 2022
If you think my article is useful to you, please feel free to appreciate