java lambda表达式函数式接口详解

Lambda表达式

​ Lambda适用于只有一个抽象方法的接口 即函数式接口

Lambda 表达式是一种匿名函数(不是匿名内部类),简单地说,它是没有声明的方法,也即没有访问修饰符、返 回值声明和名字。它实质属于函数式编程的概念。

语法:

(parameters) -> expression或 (parameters) ->{ statements; }
参数:要重写的方法的形参列表
-> :lambda运算符
表达式/语句体:要实现的方法的方法体

自定义lambda表达式实现接口

先定义一个只有抽象方法的接口 其实际上是一个函数式接口 可在接口前面加上@FunctionalInterface注解进行验证

@FunctionalInterface
interface Fu{
    void add();

}

再使用匿名内部类实现接口

public class LambdaTest {
    public static void main(String[] args) {
        //匿名内部类
       Fu f = new Fu() {
           @Override
           public void add() {
               System.out.println("f");
           }
       };
       f.add(); //f


    }

}

由于函数式接口中只必须实现一个方法 那么我们就可使用lambda将此方法名省略

public class LambdaTest {
    public static void main(String[] args) {
        //匿名内部类
       Fu f = new Fu() {
           @Override
           public void add() {
               System.out.println("f");
           }
       };
       f.add(); //f
       //lambda表达式
       Fu fl = ()->{
           System.out.println("lambda");
       };
       //Fu fl = ()->System.out.println("lambda");
        //多态调用
       fl.add(); //lambda

    }
}

()相当于add参数位置 当只有一个参数时可省略 {} 相当于方法体 当只有一句时return和{}可省略 它可看作是上面匿名类的简写

public class LambdaTestB {
    public static void main(String[] args) {
        //Fu2 fu  = (a)->{return a*5};
        Fu2 fu  = a->a*5;
        System.out.println(fu.add(100));
    }
}
interface Fu2{
    int add(int a);

}

java内部方法使用

集合的forEach是最常用lambda的地方 以它为例

查看forEach源码 可以看到是一个Consumer类型的参数 ctrl+左键进到Consumer中 可看到是一个函数式接口 到这一步可以看到跟我们上面自定义的接口几乎没什么区别

public class LambdaTestC {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,2,4,5,12,56,121);       

        list.forEach(v-> System.out.println(v));
        //forEach源码方法
         public void forEach(Consumer<? super E> action) {}
       // Consumer 源码
        @FunctionalInterface
        public interface Consumer<T> {

            /**
             * Performs this operation on the given argument.
             *
             * @param t the input argument
             */
            void accept(T t);
        }
        
    }
}

使用内部类和lambda实现

public class LambdaTestC {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,2,4,5,12,56,121);
        System.out.println("===========内部匿名类===============");
        Consumer<Integer> co = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        };
        list.forEach(co);
        //简写
        System.out.println("=============lambda================");
        Consumer<Integer> co1 = v-> System.out.println(v);
        list.forEach(co1);
        System.out.println("=============lambda2================");
        //省略赋值操作最终简写
		list.forEach(v-> System.out.println(v));
        
    }
}

函数式接口

函数式接口:接口中只能有一个抽象方法,其他的可以有default、static、Object里继承的方法等。

​ 作用:在Java中主要用在Lambda表达式和方法引用(想使用Lambda表达式, 接口必须为函数式接口)。

​ JDK8专门提供了@FunctionalInterface注解,用来进行编译检查。

@FunctionalInterface
public interface FuncInterface {
  //只有一个抽象方法
  public void  method1();
  //default方法不计
  default  void method2(){
  }
  //static方法不计
  static void method3(){
  }
  //从Object继承的方法不计
  public boolean equals(Object obj);
}
内置的函数式接口

​ JDK 也提供了大量的内置函数式接口,使得 Lambda 表达式的运用更加方便、高效。这些内置的函数式接口已经可以解决我们开发过程中绝大部分的问题,只有一小部分比较特殊得情况需要我们自己去定义函数式接口。在这里特别介绍四个函数式接口。

  1. Consumer:消费型接口(void accept(T t))。有参数,无返回值 (上文forEach的参数类型就是Consumer)

  2. Supplier:供给型接口(T get())。只有返回值,没有入参

  3. Function<T, R>:函数型接口(R apply(T t))。一个输入参数,一个输出参数,两种类型不可不同、可以一致

  4. Predicate:断言型接口(boolean test(T t))。输入一个参数,输出一个boolean类型得返回值

函数式接口方法名输入参数输出参数参数/吃草 返回/挤奶
消费型接口Consumervoid accept(T t)Tvoid只吃草不挤奶
供给型接口SupplierT get()voidT只挤奶不吃草
函数型接口FunctionR apply(T t)TR又吃草又挤奶
断言型接口Predicateboolean test(T t)TbooleanBoolean类型
消费型接口
public class TestFunctional1 {
  public static void main(String[] args) {
    List<Integer > list = new ArrayList<>();
    Collections.addAll(list,34,56,89,65,87);
    
    //使用匿名内部类实现
    Consumer consumer = new Consumer<Integer>() {
      @Override
      public void accept(Integer elem) {
        System.out.println(elem);
      }
    };
    list.forEach(consumer);
    
    //使用Lambda表达式
    //list.forEach((elem)->{System.out.println(elem);});
    list.forEach((elem)->System.out.println(elem));
  }
}
断言型接口
public class TestFunctional2 {
  public static void main(String[] args) {
    List<Integer > list = new ArrayList<>();
    Collections.addAll(list,34,56,89,65,87);

    //使用匿名内部类实现
    System.out.println(list);
    Predicate predicate = new Predicate<Integer>(){
      @Override
      public boolean test(Integer i) {
        if(i<60){
          return true;
        }
        return false;
      }
    };
    list.removeIf(predicate);
    System.out.println(list);

    //使用Lambda表达式实现
    list.removeIf((i)->{
      if(i > 80) {
      	return true;
    	}
      return false;
    });
      //list.removeIf(i->i>80); 
    System.out.println(list);
  }
}

方法引用

有时候,Lambda体可能仅调用一个已存在方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。方法引用是一个更加紧凑,易读的 Lambda 表达式,注意方法引用是一个 Lambda 表达式,方法引用操作符是双冒号 “::”。

public class LambdaTestC {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,2,4,5,12,56,121);
        System.out.println("===========内部匿名类===============");
        Consumer<Integer> co = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        };
        list.forEach(co);
        //简写
        System.out.println("=============lambda================");
        Consumer<Integer> co1 = v-> System.out.println(v);
        list.forEach(co1);
        System.out.println("=============lambda2================");
        //省略赋值操作最终简写
		list.forEach(v-> System.out.println(v));
        //方法引用
        list.forEach(System.out::println);

    }
}

方法引用2

public class FunctionalTestA {
    public static void main(String[] args) {
        Comparator<Integer> co = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
//                return o1-o2;
            }
        };
        int i = co.compare(1, 20);
        System.out.println(i);
        System.out.println("===========lambda=============");
        Comparator<Integer> co2 = (x,y)->Integer.compare(x,y);
        System.out.println(co2.compare(1, 20));
        System.out.println("=============方法引用=================");
        Comparator<Integer> co3 = Integer::compare;
        System.out.println(co3.compare(1, 20));
;
    }
}

​ 方法引用有下面几种方式:

  1. 对象引用::实例方法名

  2. 类名::静态方法名

  3. 类名::实例方法名

  4. 类名::new (也称为构造方法引用)

  5. 类型[]::new (也称为数组引用)

未完待续…

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值