Lambda表达式与函数式接口

Lambda表达式与函数式接口

一、Lambda表达式

1、@FunctionalInterface注解

@FunctionalInterface //使用注解的接口有且只能有一个方法
interface Person{
  void eat();
}

2、Lambda表达式使用

使用lambda表达式的接口使用@FunctionalInterface注解,或者接口中的方法只能有一个方法

public class Lambda {
  public static void main(String[] args) {
    //JDK1.7 使用匿名内部类
    new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 10; i++) {
          System.out.println(i);
        }
      }
    }).start();
    //JDK1.8 使用lambda表达式
    new Thread(()->{
      for (int i = 0; i < 10; i++) {
        System.out.println(i);
      }
    }).start();
  }
}

3、Lambda的方法引用

1)引用静态方法: 类名称 :: static方法名称;

2)引用某个实例对象的方法: 实例化对象 :: 普通方法;

3)引用特定类型的方法: 特定类 :: 普通方法;

4)引用构造方法: 类名称 :: new ;

当lambda参数和方法体所调用方法的参数一致(有且只调用一个方法)

class Dog {
  int total = 10;
  // 第一个参数为this
  int eat(Dog this,int num) {
    return this.total - num;
  }
}
public class MethodQuote {
  public static void main(String[] args) {
    // 方法引用
    Consumer<String> consumer = System.out::println;
    consumer.accept("data");
    // 静态方法的方法引用
    Function<Integer, String> function = String::valueOf;
    String s = function.apply(100);
    System.out.println(s);
    // 对象实例方法引用
    // 非静态方法,会默认将对象的实例化this传入实例对象方法
    String str = "abc";
    Supplier<String> supplier = str::toUpperCase;
    System.out.println(supplier.get());
      
    //这是一个普通方法,如果要引用普通方法,则往往都需要实例化对象,但是如果说现在你不想给出实例化对象,只是想引用这个方法,则就可以使用特定类来进行引用处理。
    // 使用类名做对象实例方法引用
    Function<String,String> func = String::toUpperCase;
    String s1 = func.apply(str);
    System.out.println(s1);
    // 使用类名做对象实例方法引用
    Dog dog = new Dog();
    BiFunction<Dog,Integer,Integer> biFunction = Dog::eat;
    Integer res = biFunction.apply(dog, 2);
    System.out.println(res);
    // 构造函数的方法引用
    Function<String,String> funcStr = String::new;
    String abc = funcStr.apply("new");
    System.out.println(abc);
  }
}
// 结果
data
100
ABC
ABC
8
new

注意点

class Dog {
  int total = 10;
  // 第一个参数为this
  int eat(Dog this,int num) {
    return this.total - num;
  }
}
public class MethodQuote {
  public static void main(String[] args) {
    // 非静态方法,会默认将对象的实例化this传入实例对象方法
    Dog dog = new Dog();
    int res = dog.eat(1);
    System.out.println(res);
  }
}

4、Lambda的类型推断

public class LambdaTest {

  @FunctionalInterface
  interface IIncrease{
    int add(int x,int y);
  }

  public static void main(String[] args) {
    //变量类型定义
    IIncrease iIncrease = (x,y)->x+y;
    //数组
    IIncrease[] iIncreases = {Integer::sum}; //对象实例的引用
    //强转
    Object o = (IIncrease)(x,y)->x+y;
    //返回值推断
    IIncrease method = method();

  }

  public static IIncrease method(){
    return (x,y)->x+y;
  }
}

注意:在方法有重载时,传入lambda表达式会有歧义,需要制定类型。

public class LambdaTest {

  @FunctionalInterface
  interface IIncrease{
    int increase(int x,int y);
  }
  @FunctionalInterface
  interface IDecrease{
    int decrease(int x,int y);
  }

  public static void main(String[] args) {
    LambdaTest lambdaTest = new LambdaTest();
    // 默认使用类型推断
    int res1 = lambdaTest.test((IIncrease) (x, y) -> x + y);
    int res2 = lambdaTest.test((IDecrease) (x, y) -> x - y);
    System.out.println(res1);
    System.out.println(res2);

  }
  public int test(IIncrease iIncrease){
    return iIncrease.increase(1,2);
  }
  public int test(IDecrease iDecrease){
    return iDecrease.decrease(3,4);
  }
}

5、Lambda的变量引用

java在调用方法时是值传递不是引用传递。

public class FiledQuote {
  public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>(); //list的引用不能改变
    Consumer<String> consumer = s-> System.out.println(s+list);
    consumer.accept("aa");
  }
}

如果修改list的指向对象就会编译就会报错:Variable used in lambda expression should be final or effectively final

public class FiledQuote {
  public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list = new ArrayList<>(); 	//改变list的指向
    Consumer<String> consumer = s-> System.out.println(s+list);//Variable used in lambda expression should be final or effectively final
    consumer.accept("aa");
  }
}

**原理:**在lambda外面的list变量和里面的list变量同时指向new ArrayList<>();这个对象。如果改变外面list的指向,再对list进行修改元素,对lambda里面的list没有任何影响。所有在java编译时就报错,避免了这种错误。

二、函数式接口

一些常见的函数式接口,就在java.util.function包下面

接口输入参数返回值类型说明
PredicateTboolean断言
Function<T, R>TR输入T输出R的函数
ConsumerT/消费一个数据
Supplier/T提供一个数据
UnaryOperatorTT输入输出类型相同
BiFunction<T, U, R>(T, U)R2个输入1个输出的函数
BinaryOperator(T,T)T2个输入1个输出的函数(输入输出相同)
BiConsumer<T, U>(T,U)/消费两个数据
BiPredicate<T, U>(T, U)boolean可以传入两个参数的断言

1、Predicate

1)基本使用
public class PredicateTest {
  public static void main(String[] args) {
    //断言
    Predicate<Integer> predicate = i -> i > 0; //调用test() 大于0为true
    System.out.println(predicate.test(5));// 结果true
    Predicate<Integer> other = i -> i > 3;  //调用test() 大于3为true

    // && 运算 两个Predicate都必须返回true,才为true
    Predicate<Integer> and = predicate.and(other);
    System.out.println(and.test(2));// 结果 false

    // || 运算 两个Predicate一个返回true,结果为true
    Predicate<Integer> or = predicate.or(other);
    System.out.println(or.test(2));// 结果true

    // 取反 !非运算
    Predicate<Integer> negate = predicate.negate();
    System.out.println(negate.test(-1));// 结果true

    // ==运算
    Predicate<Integer> equal = Predicate.isEqual(null);
    System.out.println(equal.test(null)); // 结果true

    // 取反
    Predicate<Integer> not = Predicate.not(predicate);
    System.out.println(not.test(1));// 结果true
  }
}
2)使用实例

**需求:**取出在1~20间 查找出(5,15]区间的偶数

// 取出在1~20间 查找出(5,15]区间的偶数
    int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
    List<Integer> list=new ArrayList<>();
    for(int i:numbers) {
      list.add(i);
    }
    Predicate<Integer> p1=i->i>5;
    Predicate<Integer> p2=i->i<=15;
    Predicate<Integer> p3=i->i%2==0;
    List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
    System.out.println(test.toString());

//结果
[6, 8, 10, 12, 14]

如果我们现在需求变量需要找出(5,15]区间的奇数数,我们就可以使用negate() 和 not()方法

// 只需将修改成如下
// 方法1:
List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
// 方法2:
List test=list.stream().filter(p1.and(p2).and(Predicate.not(p3))).collect(Collectors.toList());
//not() 方法底层也是调用p3的negate()方法
//结果
[7, 9, 11, 13, 15]

如果我们现在需求又加了一项,筛选出结果中某一个值

List test=list.stream().filter(p1.and(p2).and(Predicate.not(p3).and(Predicate.isEqual(7)))).collect(Collectors.toList());
//结果
[7]

2、Consumer

类型扩展Consumer接口有:

IntConsumer

DoubleConsumer

LongConsumer

1)基本使用
public class ConsumerTest {
  public static void main(String[] args) {
    // 消费一个数据
    Consumer<Integer> consumer = i-> System.out.println(i);
    consumer.accept(1);

    System.out.println("--------------------------------");
    Consumer<Integer> after = i-> System.out.println(i+i);
    Consumer<Integer> andThen = consumer.andThen(after);
    andThen.accept(1);
    System.out.println("--------------------------------");
    Consumer<Integer> after1 = i-> System.out.println(i+i+i);
    Consumer<Integer> andThen1 = andThen.andThen(after1);
    andThen1.accept(1);
  }
}
// 调用andThen()可进行链式消费
// 结果
1
--------------------------------
1
2
--------------------------------
1
2
3
2)使用案例
public class ConsumerTest {
  public static void main(String[] args) {
    String[] UserInfo = {"张三,19","李四,20","王五,18"};
    printUser(UserInfo,(user)->{
      String name = user.split(",")[0];
      System.out.print("姓名:"+name+" ");
    },(user)->{
      String age = user.split(",")[1];
      System.out.println("年龄:"+age);
    });

  }

  public static void printUser(String[] arr,Consumer<String> consumer,Consumer<String> consumer1){
    for (String usr : arr) {
      consumer.andThen(consumer1).accept(usr);
    }
  }
}
// 结果
姓名:张三 年龄:19
姓名:李四 年龄:20
姓名:王五 年龄:18

3、Supplier

类型扩展Supplier接口有:

BooleanSupplier

IntSupplier

DoubleSupplier

LongSupplier

1)基本使用
public class SupplierTest {
  public static void main(String[] args) {
    Supplier<Integer> supplier = ()->2;
    System.out.println(supplier.get());
  }
}
// 结果
2
2)使用案例

求数组中的最大值

public class SupplierTest {
  public static void main(String[] args) {
    int nums[] = {1,9,8,11,45,33,6,9};
    int max = getMax(()->{
      int maxTemp = nums[0];
      for (int i = 0; i < nums.length; i++) {
        if(nums[i]>maxTemp)
          maxTemp=nums[i];
      }
      return maxTemp;
    });
    System.out.println(max);
  }

  public static int getMax(Supplier<Integer> supplier){
    return supplier.get();
  }
}
// 结果
45

4、Function<T, R>

输入值类型扩展Function接口有:

IntFunction 输入值必须为int

DoubleFunction 输入值必须为double

LongFunction 输入值必须为long

输出值类型扩展Function接口有:

ToIntFunction 输出值必须为int

ToDoubleFunction 输出值必须为double

ToLongFunction 输出值必须为long

输入出值类型扩展Function接口有:

IntToDoubleFunction 输人值必须为int,输出值必须为double

IntToDoubleFunction 输人值必须为int,输出值必须为long

DoubleToIntFunction 输人值必须为double,输出值必须为int

DoubleToLongFunction 输人值必须为double,输出值必须为long

LongToDoubleFunction 输人值必须为long,输出值必须为double

LongToIntFunction 输人值必须为long,输出值必须为int

1)基本使用
public class FunctionTest {
  public static void main(String[] args) {
    Function<Integer,String> fun = i->i+" hello";
    System.out.println(fun.apply(7));

    //fun在fun1之前执行
    Function<String,String> fun1 = i->i+" word";
    Function<Integer, String> compose = fun1.compose(fun);
    System.out.println(compose.apply(7));

    //fun2在fun1后执行
    Function<String,Character> fun2 = i->i.charAt(0);
    Function<String, Character> andThen= fun1.andThen(fun2);
    System.out.println(andThen.apply("hello"));

    //传入一个什么值就返回一个什么值
    Function<Integer, Integer> id = Function.identity();
    System.out.println(id.apply(3));
  }
}
// 结果
7 hello
7 hello word
h
3
2)使用案例
public class FunctionTest {
  static class User {
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
      this.name = name;
      this.age = age;
    }

    public String getName() {
      return name;
    }

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

    public Integer getAge() {
      return age;
    }

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

  public static void main(String[] args) {
    Function<User, String> function = User::getName;  //user->user.getName()的引用写法
    List<User> userList = new ArrayList<>();
    userList.add(new User("张三", 18));
    userList.add(new User("李四", 22));
    userList.add(new User("王五", 25));
    List<String> userName = getUserName(userList, function);
    for (String name : userName) {
      System.out.println(name);
    }
  }

  public static List<String> getUserName(List<User> userList, Function<User, String> func) {
    List<String> result = new ArrayList<>();
    for (User user : userList) {
      String name = func.apply(user);
      result.add(name);
    }
    return result;
  }

}
// 结果
张三
李四
王五

5、UnaryOperator

Function<T, R>的子类,返回类型和传值类型一致

类型扩展UnaryOperator接口有:

IntUnaryOperator

DoubleUnaryOperator

LongUnaryOperator

1)基本使用
public class UnaryOperatorTest {
  public static void main(String[] args) {
    UnaryOperator<Integer> unaryOperator = i->i*3;
    System.out.println(unaryOperator.apply(3));

    UnaryOperator<Integer> identity = UnaryOperator.identity();
    System.out.println(identity.apply(5));
  }
}
// 结果
9
5

6、BiFunction<T, U, R>

1)基本使用
public class BiFunctionTest {
  public static void main(String[] args) {
    BiFunction<Integer,Double,String> biFunction = (i,j)->i+""+j;
    System.out.println(biFunction.apply(1, 2.1));
    Function<String,Double> after = Double::parseDouble;
    BiFunction<Integer, Double, Double>  andThen= biFunction.andThen(after);
    Double res = andThen.apply(2, 2.1);
    System.out.println(res);
    System.out.println(res.getClass().getName());
  }
}
// 结果
12.1
22.1
java.lang.Double

7、BinaryOperator

BinaryOperator继承BiFunction<T, T, T>

类型扩展BinaryOperator接口有:

IntBinaryOperator

DoubleBinaryOperator

LongBinaryOperator

1)基本使用
public class BinaryOperatorTest {
  public static void main(String[] args) {
    // 两个输入 一个输出 类型都相同
    BinaryOperator<Integer> binaryOperator = (i,j)->i*j;
    System.out.println(binaryOperator.apply(3, 4));

    // 求两个数最大值
    BinaryOperator<Integer> maxBinaryOperator = BinaryOperator.maxBy(Integer::compareTo); //实例对象的引用
    System.out.println(maxBinaryOperator.apply(1, 2));
    
    // 求两个数最小值
    BinaryOperator<Integer> minBinaryOperator = BinaryOperator.minBy((a,b)->a.compareTo(b));
    System.out.println(minBinaryOperator.apply(1, 3));
  }
}
// 结果
12
2
1

8、BiConsumer<T, U>

1)基本使用
public class BiConsumerTest {
  public static void main(String[] args) {
    BiConsumer<String,String> biConsumer = (i,j)-> System.out.println(i+" "+j);
    BiConsumer<String,String> after = (i,j)-> System.out.println(j+" "+i);
    biConsumer.accept("hello","world");
    System.out.println("------------------------------");
    BiConsumer<String, String> biConsumer2 = biConsumer.andThen(after);
    biConsumer2.accept("hello","world");
  }
}
// 结果
hello world
------------------------------
hello world
world hello
9、BiPredicate<T, U>
public class BiPredicateTest {
  public static void main(String[] args) {
    BiPredicate<Integer, String> biPredicate = (i, j) -> i > 0 && j.length() > 1;
    System.out.println(biPredicate.test(1, "a"));
    System.out.println(biPredicate.test(1, "ab"));
  }
}

其他方法使用与Predicate一样

三、级联表达式和柯理化

public class CurryTest {
  public static void main(String[] args) {
    // 级联表达式
    Function<Integer,Function<Integer,Function<Integer,Integer>>> func = x->y->z->x+y+z;
    // 柯理化:将多个参数的函数转成一个参数的函数链
    // 目的:把函数标准化
    Integer res = func.apply(1).apply(2).apply(3);
    System.out.println(res);

    // 函数标准化的好处
    int[] nums = {1,2,3,4,5,6};
    Function f= func;
    for (int i = 0; i < nums.length; i++) {
      if(f instanceof Function){
        Object obj = f.apply(nums[i]);
        if(obj instanceof Function){
          f = (Function)obj;
        }else {
          System.out.println("结果:"+obj);
        }
      }
    }6
  }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值