Lambda的引入体验
lambda是java8之后的一个新的特性。我当时学java的时候还没有见过这个表达式。主要是lambda简化了代码块,在某些方面,是比匿名内部类更加方便地。但是并不能完全替代匿名内部类。在使用地方面,还是有区别地,具体的,后面说。
还记得自己创建线程吗?一种是自己去继承Thread然后实现run()方法。这是基本的,如果还需要自己再重写一些方法,也可以去实现。就像这样。
package demo.LambdaDemo; public class LambdaDemo extends Thread{ @Override public void run() { for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public LambdaDemo(String name) { super(name); } public LambdaDemo() { super(); } public static void main(String[] args) { LambdaDemo ld = new LambdaDemo("线程1"); LambdaDemo ld1 = new LambdaDemo("线程2"); ld.start(); ld1.start(); }
还可以去实现Runable接口
先定义一个类,实现Runable接口。
package demo.LambdaDemo; public class MyRunnable implements Runnable { @Override public void run() { } }
然后具体类似这样的格式
package demo.LambdaDemo; public class MyRunnable implements Runnable { @Override public void run() { } public static void main(String[] args) { MyRunnable my = new MyRunnable(); Thread th = new Thread(my); th.start(); } }
还有一种是采用匿名内部类
new Thread(new Runnable() { @Override public void run() { System.out.println(""); } }).start();
这样简化了操作。但是lambda比这个还要简化。
new Thread(() -> { System.out.println("..."); }).start();
对比上面的说明格式,你应该注意到这种格式。lambda里面有一个(),括号里面要求的是一种形式参数,但是你可以注意到我们这里英勇的接口没有使用形式参数,所以可以省略掉,然后剩下的就是你想做的事情了。所以就是这样的格式。
在这里,我们先体验一下,然后下面展开说明一些基本的使用具体实例。
实例一(抽象方法无参无返回值)
首先定义一个接口
package demo.LambdaDemo; public interface Eatable { void eat(); }
在测试类调用
我们可以去使用一个方法将接口参数传入
private static void useEatable(Eatable e) { e.eat(); }
然后你这样调用的话,其实还是没有具体实现。那么我们去具体实现一下。
采用匿名内部类
//匿名内部类实现 useEatable(new Eatable() { @Override public void eat() { System.out.println("...."); } });
对应采用的lambda表达式
useEatable(()->{ System.out.println("..."); });
useEatable是我们定义的方法,我们的这个方法对应到接口Eatable接口,然后Eatable里面的这个方法并没有参数,我们就不需要传参,所以可以直接在{}里面做我们需要完成的事情。
实例二(抽线方法有参无返回值)
定义的接口类
package demo.LambdaDemo; public interface Flayable { void fly(String s); }
可以注意到这个接口中的方法是有参的
还是在测试类中写一个方法
private static void useFlyable(Flayable f) { f.fly("。。。。。。。"); }
可以采用内名内部类对接口方法具体实现
//匿名内部类 useFlyable(new Flayable() { @Override public void fly(String s) { System.out.println(";;;;;;;;"); } });
采用lambda表达式
//lambda表达式 useFlyable((String s)->{ System.out.println(s); System.out.println("....."); });
实例三(抽象方法带参带返回值)
比如这样
package demo.LambdaDemo; public interface Addable { int add(int x,int y); }
这是一个带参带返回值的抽象方法
然后匿名内部类实现
useAddable(new Addable() { @Override public int add(int x, int y) { return x-y; } });
对的,这个方法具体实现在这里,具体的功能还是由我们自己具体实现来定。
lambda表达式 useAddable((int x,int y)->{ return x+y; });
这是基本的写法。
lambda的表达式的简化操作
在特定条件下可以省略一些东西,变得更加简化。
1:参数类型可以省略(但是有多个参数的情况下,不能只省略一个)
如下,我们还是定义一个接口类
package demo.LambdaDemo.lambdaDemo01; public interface Addable { int add(int x,int y); }
然后测试类定义一个方法,当然在这里你也可以去简单实现
private static void useAddable(Addable a) { int sum = a.add(10,20); System.out.println(sum); }
lambda常规
useAddable((int x,int y)->{ return x+y; });
lambda简化
useAddable((x,y)->{ return x+y; });
注意这里省略的是参数类型,不是参数。
2:如果有且仅有一个参数,那么小括号可以省略
我们定义一个接口类
package demo.LambdaDemo.lambdaDemo01; public interface Flyable { void fly(String s); }
测试类定义方法
private static void useFlyable(Flayable f) { f.fly("......"); } private static void useAddable(Addable a) { int sum = a.add(10,20); System.out.println(sum); }
然后测试类操作
lambda常规
useFlyable((String s)->{ System.out.println("...."); });
lambda简化
useFlyable(s->{ System.out.println("...."); });
3:如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
useFlyable(s-> System.out.println("...."));
如果有return ,return也要省略掉
useFlyable(s-> System.out.println("...."));
完整测试代码
接口
package demo.LambdaDemo.lambdaDemo01; public interface Addable { int add(int x,int y); }
package demo.LambdaDemo.lambdaDemo01; public interface Flyable { void fly(String s); }
测试类,部分代码已经注释。
package demo.LambdaDemo.lambdaDemo01; import demo.LambdaDemo.Flayable; public class LambdaDemo { public static void main(String[] args) { // useAddable((int x,int y)->{ // return x+y; // }); //省略 // //1:参数类型可以省略(但是有多个参数的情况下,不能只省略一个) // useAddable((x,y)->{ // return x+y; // }); // useFlyable((String s)->{ // System.out.println("...."); // }); // useFlyable((s)->{ // System.out.println("...."); // }); //2:如果有且仅有一个参数,那么小括号可以省略 // useFlyable(s->{ // System.out.println("...."); // }); // 3:如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉 // useFlyable(s-> System.out.println("....")); //如果有return ,return也要省略掉 useAddable((x,y)->x+y); } private static void useFlyable(Flayable f) { f.fly("......"); } private static void useAddable(Addable a) { int sum = a.add(10,20); System.out.println(sum); } }
匿名内部类和lambda的使用区别
lambda的使用是比较严格的,要求有接口,还必须只有一个抽象方法。并且lambda必须是只能实现接口。在用法上,必须要有上下文环境。
这就说明的是,lambda表达式虽然可以省略某些内容,但是你必须提供上下文化境,让lambda可以推导出你是使用了什么接口,接口中的参数是什么等等。
简单的说,就是如果你没有定义接口,你直接使用lambda是不行的。接口或者接口中的方法可以认为是可以提供的环境,这样,省略的时候可以推导出来,下文的环境,就是基本你去实现的具体过程。
匿名内部类的要求比较宽泛。因为它的类型是都可以的。抽象类,接口,具体类都可以。在原理上,就是匿名内部类会在执行后生成一个新的字节码文件,但是lambda并不会,lambda是动态生成的。
具体测试
package demo.LambdaDemo.lambdaDemo02.lambdaDemo03; public interface Inter { void show(); }
package demo.LambdaDemo.lambdaDemo02.lambdaDemo03; public class Student { public void study() { System.out.println("...."); } }
package demo.LambdaDemo.lambdaDemo02.lambdaDemo03; public abstract class Animal { public abstract void method(); }
package demo.LambdaDemo.lambdaDemo02.lambdaDemo03; public class LambdaDemo { 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(){ // public void study(){ // System.out.println("具体类"); // } // });//也可以是具体类 //使用lambda只能是接口 useInter(()-> System.out.println("接口")); //如果接口中有一个以上的抽象方法,那么就无法使用lambda表达式,只能用匿名内部类 } private static void useStudent(Student s) { s.study(); } private static void useAnimal(Animal a) { a.method(); } private static void useInter(Inter i) { i.show(); } }