了解lambda之前先了解下什么是函数式接口,函数式接口即接口里必须有一个抽象方法(抽象的方法只能有一个,可以有其他的用default修饰的方法以及从Object继承的方法)
jdk8里新增了一个@FunctionalInterface注解,这个注解标注此接口为函数式接口,但是并不是必须的,任何满足我上面所说的只有一个抽象方法的接口都可以称之为函数式接口,但是如果一个接口上标注了此注解,就必须满足上述条件
lambda表达式用来重写函数式接口中的那个抽象方法,lambda直接看做匿名抽象类,因为lambda可以对接口赋值
lambda表达式的语法包括3个部分:
1.参数列表(...) 如(String s,Integer i) () (s) 等,没有参数时就是(),只有一个参数时可以写成str,省略掉参数的小括号
2.箭头 ->
3.执行的代码 { },执行的代码只有一行时,可以省略大括号
4.需要注意的是lambda无法在其方法体中修改局部变量的值
lambda表达式整体看作一个某个接口的匿名类,lambda的参数,方法体是该匿名类的下唯一重写抽象方法的参数,方法体,lambda是对匿名类的简化
下面以创建线程为例
1 //匿名内部类创建线程
2 new Thread(new Runnable() { 3 @Override 4 public void run() { 5 System.out.println("匿名内部类启动线程"); 6 } 7 }).start(); 8
9 //使用lambda表达式启动一个新的线程 10 //()对应run方法的参数 11 //->后面表示run的方法体
12 new Thread(()->System.out.println("使用lambda启动线程")).start();
来看下jedis中execute方法关于lambda的例子
从上图可以看到execute方法需要一个RedisCallBack,这是一个接口,内部只有一个抽象方法,虽然没有@FunctionalInterface注解,但这是一个抽象接口
唯一的抽象方法doInRedis需要一个RedisConnection参数,返回一个泛型参数,我们抽取下excute需要的关于语法的关键代码
1 execute((RedisCallback<List<Object>>) connection -> { 2 connection.openPipeline(); 3 return xx; 4 });
connection -> {connection.openPipeline();return xx;}); connection是doInRedis的形参,{}中doInRedis的方法体,而connection前面的(RedisCallback<List<Object>>)是对lambda的类型说明(RedisCallback<T>)
下面再举一个Runnable接口的例子
1 /**
2 * lambda语法包括3个部分: 3 * (参数列表)->{方法体;} 4 * 只有一个参数时,()可以省略 5 * 方法体只有一行时可以省略{;} 6 * @author tele 7 * 8 */
9 public class Demo1 { 10 public static void main(String[] args) { 11 List<Integer> list = new ArrayList<Integer>(); 12 list.add(1); 13 list.add(2); 14 list.add(3); 15 //遍历list
16 list.forEach(i->System.out.println(i)); 17
18
19 //>=可以直接判断符号
20 /* int a = 1; 21 int b = 2; 22 System.out.println(b>=a?"b>a":"b<a");*/
23
24 //匿名内部类创建线程
25 new Thread(new Runnable() { 26 @Override 27 public void run() { 28 System.out.println("匿名内部类启动线程"); 29 } 30 }).start(); 31
32 //使用lambda表达式启动一个新的线程 33 //()对应run方法的参数 34 //->后面表示run的方法体
35 new Thread(()->System.out.println("使用lambda启动线程")).start(); 36
37
38 //如果要使用lambda来声明接口那么接口中只能有一个抽象方法 39 //因为函数接口是只有一个抽象方法的接口(抽象的方法只能有一个,可以有其他的用default修饰的方法)
40
41 String s = "lambda test"; 42 int i= 100; 43 MyInterface myInterface = (str,m)->{ 44 //i++; 错误,无法对i的值进行修改
45 System.out.println(i); 46 System.out.println(str + " " + m); 47 return str+"----"+str; 48 }; 49 String result = myInterface.test(s,0); 50 System.out.println(result); 51
52
53 } 54
55 //定义一个内部的函数式接口
56 static interface MyInterface { 57 //void test(String s,int i);
58 String test(String s,int i); 59 } 60 }
到这再次说明,lambda表达式是重写了接口中的抽象方法,包括第16行的代码把lambda表达式传入到foreach中,传入的lambda表达式实质是对Consumer接口的accept方法的重写
参数类型的省略是因为java的类型推断,编译器知道函数式接口中的那个抽象方法需要什么类型
下面结合Predicate接口再来深入了解下lambda表达式,这个接口主要用于断言,当然他是一个函数式接口,可以看到他的抽象方法是test(),所以我们写的lambda表达式就是重写test方法
注意这里用了泛型
先看filter方法,filter方法对list进行遍历,满足test()后进行输出,而test的方法体就是我们传入的lambda表达式,从这一点上看,使用lambda有点绕的地方就是按照以往的做法我们调用filter时出了传入list,另一个参数应该为Precidate类型,然而使用了lambda后,我们直接传入Predicate接口中的抽象方法的参数与方法体即可,即使我们传入一个Predicate类型的参数,接下来仍然是去调用test(),从这点看lambda可以说是一步到位了
完整代码,and对应&& or对应|| negate对应!
1 /**
2 * Predicate接口的使用 3 * @author tele 4 * 5 */
6 public class Demo2 { 7 public static void main(String[] args) { 8 List<String> list = Arrays.asList("java","hadoop","python","php","lucene"); 9 filter(list,s->s.startsWith("j"));//java 10 // filter(list, s->true);//全部输出
11
12
13 Predicate<String> condition1 = str->str.startsWith("j"); 14 Predicate<String> condition2 = str->str.endsWith("h"); 15
16 //and
17 Predicate<String> andCondition = condition1.and(condition2); 18 boolean result = andCondition.test("jsadh"); 19 System.out.println(result);//true 20
21 //or
22 Predicate<String> orCondition = condition1.or(condition2); 23 result = orCondition.test("jasd"); 24 System.out.println(result);//true 25
26 //negate,对判断条件取反
27 Predicate<String> negate = condition1.negate(); 28 System.out.println(negate.test("aj"));//true 29
30 //isEqual(了解,比较两个对象是否相等)
31 result = Predicate.isEqual("as").test("aaa"); 32 System.out.println(result); 33
34 } 35
36 //test方法是predicate中的唯一一个抽象方法
37 public static void filter(List<String> list,Predicate<String> predicate) { 38 list.forEach(t->{ 39 if(predicate.test(t)){ 40 System.out.println(t); 41 } 42 }); 43 } 44 }