java8 lambda表达式
来源:互联网 发布:ubuntu ssh服务安装包 编辑:程序博客网 时间:2024/06/14 22:25
为什么要用lambda表达式呢,有什么用,其实就是简化代码。
举个例子:对List排序
class A{private int num=0;A(int a){num=a;}public int getNum(){return num;}}
List<A> list = new ArrayList<A>(); list.add(new A(5)); list.add(new A(4)); list.add(new A(8)); list.add(new A(2));<!--定义list--> Comparator<A> com1 = new Comparator<A>() {<!--第一种方法-->public int compare(A o1, A o2) {return o1.getNum()-o2.getNum();} }; list.sort(com1); list.sort( <!--第二种方法-->new Comparator<A>() {public int compare(A o1, A o2) {return o1.getNum()-o2.getNum();} } ); list.sort((A o1,A o2)->o1.getNum()-o2.getNum());<!--lambda表达式--> list.sort((o1,o2)->o1.getNum()-o2.getNum());<!--根据类型推断可以去掉参数的类型--> list.sort(Comparator.comparing(A::getNum));<!--方法引用--> import static java.util.Comparator.comparing; list.sort(comparing(A::getNum));<!---静态导入后可以更简单-> list.forEach(e->System.out.println(e.getNum()));<!--如果参数只有一个类型推断时,可以去掉()-->通过上面可以看到lambda表达式可以简化代码,那么怎么用呢?
lambda表达式可以理解为一个匿名函数 ,没有名称,有参数,函数主题,返回值,可能还有一个可以抛出的异常列表 。
lambda表达式基本语法:([parameters,...]) -> expression 或者 ([parameters,...]) -> {statements;...}
例如:
() -> {} <!--表示没有参数,返回void,类似 public void run(){}--> (String s) -> s.length <!--表示有参数s,返回s的长度。return被隐藏了。--> (int a,int b) -> {return a+b;} <!--表示有两个参数a,b,返回a+b的值,有花括号返回时一定要有return--> () -> { System.out.println("hello"); System.out.println("hi"); } <!--表示没有参数,但是有两个输出语句,返回void-->
注意:有花括号时,返回一定要有return,不然报错。没有花括号返回时不要加return,不然报错。
lambda表达式可以在函数式接口上使用,函数式接口就是只定义一个抽象方法的接口(哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。)
public interface Comparator<T> { <!--是函数式接口-->int compare(T o1, T o2);}public interface Runnable{ <!--是函数式接口-->void run();}public interface Callable<V>{ <!--是函数式接口-->V call();}public interface R extends Runnable{ <!--不是函数式接口,因为有两个抽象方法-->void r();}public interface A { <!--是函数式接口,默认方法不影响,-->void a();default void b(){System.out.println("bbbbb");}}
Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。 Runnable r1 = ()-> System.out.println("r1"); Runnable r2 = new Runnable() {@Overridepublic void run() {System.out.println("r2");} }; new Thread(r1).start(); new Thread(r2).start(); new Thread(()->System.out.println("runnable")).start();
函数式接口上都有@FunctionalInterface 标注。例如:@FunctionalInterfacepublic interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run();}
简单说明一下lambda表达式怎么具体使用,首先lambda表达式是函数式接口的一个实例,是实现接口的。选择要实现的接口后,看那个接口的抽象方法,把方法的参数写到()里面,没有就不写直接一个()。然后看方法有什么返回值,把具体实现代码写到->的后面,返回方法的返回值。实现代码很多可以用{}。所以就可以写出(A a,B b)->{...}这样的lambda表达式。
函数式接口的抽象方法的签名叫做函数描述符。例如runnable接口的方法public void run(){}, 这个方法没有参数,没有返回值,所以可以用()->void表示。
lambda表达式的签名也是一样,()->System.out.println("hello"),表示没有参数,没有返回值,也就是()->void。
java自带的java.util.function里面有很多函数式接口。例如Predicate<T>,Consumer<T>,Function<T,R>等。他们的函数描述符为:
Predicate<T> (T)->boolean //参数T,返回booleanConsumer<T> (T)->void //参数T,没有返回Function<T,R> (T)->R //参数T,返回RSupplier<T> ()->T //没有参数,返回TUnaryOperator<T> (T)->T //传入T类型,返回T类型BinaryOperator<T> (T,T)->T //传入两个T类型,返回一个T类型。BiPredicate<L,R> (L,R)->boolean //传入L,R,返回booleanBiConsumer<T,U> (T,U)->void //传入T,U,没有返回BiFunction<T,U,R> (T,U)->R //传入T,U,返回R如果传入的参数是基本类型,那就只能使用基本类型的包装类型了,但是这样会装箱拆箱。如果输入输出都是基本类型,那么使用包装类型很不好,所以就有了IntPredicate,IntConsumer,IntFunction<R>这种接口,使用这些接口不用装箱拆箱。Function接口还有针对输出参数类型的变种: ToIntFunction<T>、 IntToDoubleFunction等。
IntPredicate LongPredicate DoublePredicateIntConsumer LongConsumer DoubleConsumerIntFunction<R> IntToDoubleFunction IntToLongFunction LongFunction<R> LongToDoubleFunctionLongToIntFunction DoubleFunction<R> ToIntFunction<T> ToDoubleFunction<T> ToLongFunction<T>BooleanSupplier IntSupplier LongSupplier DoubleSupplierIntUnaryOperator LongUnaryOperator DoubleUnaryOperatorIntBinaryOperator LongBinaryOperator DoubleBinaryOperatorObjIntConsumer<T> ObjLongConsumer<T>ObjDoubleConsumer<T>ToIntBiFunction<T,U> ToLongBiFunction<T,U> ToDoubleBiFunction<T,U>使用上面的接口可以解决参数是基本类型的问题,避免装箱拆箱。下面是使用函数式接口的一些例子:
布尔表达式 (List<String> list) -> list.isEmpty() Predicate<List<String>>创建对象 () -> new Apple(10) Supplier<Apple>消费一个对象 (Apple a) ->System.out.println(a.getWeight()) Consumer<Apple>从一个对象中选择/提取 (String s) -> s.length() Function<String, Integer>或ToIntFunction<String>合并两个值 (int a, int b) -> a * b IntBinaryOperator比较两个对象 (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight()) Comparator<Apple>或BiFunction<Apple, Apple, Integer>或 ToIntBiFunction<Apple, Apple>好的,现在就可以根据自己的实际情况来选择接口实现,或者自定义函数式接口来实现。例如要得到list里面A的num值大于5的A,组合成另一个list返回。分析一下只要判断A的num是否大于5就行了,那就是(A a)->boolean,所以选择predicate<A>这个接口。
public class Test { public static void main(String args[]) throws Exception { List<A> list = new ArrayList<A>(); list.add(new A(5)); list.add(new A(4)); list.add(new A(8)); list.add(new A(6)); List<A> list2 = getList(list, a->a.getNum()>5); list2.forEach(e->System.out.println(e.getNum())); } public static List<A> getList(List<A> list,Predicate<A> p){ List<A> list2 = new ArrayList<A>(); for(A a:list){ if(p.test(a)){ list2.add(a); } } return list2; }}class A{private int num=0;A(int a){num=a;}public int getNum(){return num;}}通过上面的写法就可以得到num大于5的集合了,而且这样写的好处就是以后如果判断条件改了,不是num大于5而是num大于5,小于10,直接在lambda表达式里改代码就行了,不用改getList()这个方法。
已知的函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中。
@FunctionalInterfacepublic interface A {<!--自定义函数式接口-->String process(B b) throws IOException;}Function<String, Integer> f = b -> { <!--因为Function没有抛出异常,所以可以使用try/catch-->try {return b.length()/0;}catch(Exception e) {throw e;}};
通过上面可以知道lambda 表达式怎么用,但是怎么知道lambda表达式是不是正确的呢?
类型检查,这个是通过上下文来检查的。例如上面getList(list,a->a.getNum()>5)。
1.首先你要找出getList方法的声明。
2.知道了它是Predicate<A>类型(目标类型)的对象。
3.Predicate<A>是一个函数式接口,定义了一个叫作test的抽象方法。
4.test方法描述了一个函数描述符,它可以接受一个A,并返回一个boolean。
5. 然后判断lambda表达式是不是符合接受一个A,并返回一个boolean。
注意:如果Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句也必须与之匹配。
有可能一种lambda表达式可以匹配多种接口的情况,例如(Integer a,Integer b)->a-b;匹配所有函数描述符为(Integer,Integer)->int的函数式接口。
特殊的void兼容规则:
如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容。
// Predicate返回了一个booleanPredicate<String> p = s -> list.add(s);//相当于{return list.add(s)}// Consumer返回了一个voidConsumer<String> b = s -> list.add(s); //相当于{list.add(s); return;}
上面两中都是合法的,尽管List的add方法返回了一个boolean 。如果不是语句表达式就不行了,例如:
Consumer<String> b = s -> "hello"; 。
类型推断:
Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可以在Lambda语法中省去标注参数类型。
例如: Comparator<Integer> comparator = (Integer a,Integer b)->a-b;
Comparator<Integer> comparator2 = (a,b)->a-b;
注意:当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。 例如要输出一个String类型的值。(String s)->System.out.println(s)等价于s->System.out.println(s)。
lambda表达式还可以使用外部的变量。
public class Test {static int num=5; public static void main(String args[]) throws Exception { A a = new A(10); new Thread(()->System.out.println(a.getNum())).start();<!--使用实例变量--> new Thread(()->System.out.println(num)).start();<!--使用静态变量--> int n =10; new Thread(()->System.out.println(n)).start(); <!--使用局部变量--> } }class A{private int num=0;A(int a){num=a;}public int getNum(){return num;}}如果访问局部变量,那么局部变量必须显式声明为final,或者实际上是final。
- Java8 Lambda表达式教程
- Java8 Lambda表达式教程
- Java8: Lambda表达式语法
- java8 Lambda表达式
- Java8 Lambda表达式教程
- Java8 Lambda表达式
- java8 Lambda表达式
- Java8 Lambda表达式教程
- Java8 Lambda表达式教程
- Java8 Lambda表达式教程
- java8 lambda表达式-语法
- java8 lambda表达式-其他
- java8 lambda表达式
- Java8 Lambda表达式入门
- Spark/Java8 lambda表达式
- Java8 Lambda表达式教程
- Java8 lambda表达式
- Java8 Lambda表达式教程
- CSUOJ
- ES6解构赋值
- java基础复习--复习总结5
- div自适应高度
- PHP利用phpExcel实现Excel数据的导入导出
- java8 lambda表达式
- Simulink之S-function函数笔记之一
- 73. Set Matrix Zeroes
- 高效模糊查询like小结
- python爬虫:常用浏览器的useragent
- 微信小程序详细教程
- 安卓系统常用命令
- HDU 6071 Lazy Running(同余+最短路)
- 接入华为推送,开发流程, 与遇到的坑(PUSH SDK 和 HMS SDK)