最近几天学习了一下lambda表达式,看了不少博客,感觉有一篇博客总结的一句话总结的很好:lambda表达式是一段可以传递的代码,它的核心思想是将面向对象中的传递数据变成传递行为。其实以前也有传递行为的方式,c++里是函数指针,java中有一种神奇的东西叫做匿名内部类,而现在的lambda表达式,提供了一种取代匿名内部类的途径,并且将其更规范化的变成了一类接口——函数是接口。以下将分为三个大的部分进行介绍,第一部分将对lambda表达式的语法格式进行介绍,第二个部分将继续第一篇对函数式接口的介绍去大体介绍一下java.util.function,第三个部分会举例子

lambda表达式基本语法

lambda表达式的基本格式有两种:

(params)->expression 或者 (params)->{statements;}

对于这两种表达,有以下四条语法约束:

1、参数可选类型声明:可以不写出参数的类型,编译器会进行自动推断

2、可选参数圆括号:当只有一个从参数的时候,可以不加圆括号

3、可选大括号:当整个lambda函数体只有一行语句的时候,可以省略掉大括号

4、可选的返回关键字:当只有一条语句的时候,可以不显示的写出return

为了让大家明白上述规则,我们来看下面代码段(注意,当使用泛型的时候一定要传入类,不要传入基本类型

 public class LambdaExpTest {

     @FunctionalInterface
interface LambdaFuncInterface<T> {
public abstract T function(T x, T y);
} @FunctionalInterface
interface TestInterface<T> {
public abstract T function(T x);
} public static void main(String[] args) {
/**
* 我们可以看下面这个是最完整的lambda表达式,在上面的函数式接口中有一个两个参数的方法,所以这里也是声明了Integer类型的x和y
* 函数体有两句话,第一句两个数相减,第二句话返回这个值
*/
LambdaFuncInterface<Integer> helper = (Integer x, Integer y)->{
int tmp = x - y;
return tmp;
};
System.out.println(helper.function(10, 15)); /**
* 写成这个样子也是可以的,但是要注意的是一定要两个都不写,或者两个都写,如果写成(Integer x, y)这样子的话就会报错了
*/
helper = (x, y)->{
int tmp = x - y;
return tmp;
}; /**
* 写成这样也是可以的,就是只有一行函数体,然后就可以不写return和返回值
*/
helper = (x, y)-> x - y; /**
* 当我们的函数接口只需要一个参数的时候,可以省略掉参数的括号,一下两种写法都对
*/
TestInterface<Integer> helper2 = (x) -> -x;
helper2 = x -> -x;
} }

函数式接口java.util.function

java.util.function是jdk1.8新加入的一个函数式接口的包,在jdk1.8之前也有几个函数式接口,在这里给大家罗列一下

java.long.Runnable

java.util.concurrent.Callable

java.util.Comparator

java.io.FileFilter

java.security.PrivilegedAction

java.nio.file.PathMatcher

java.lang.reflect.InvocationHandler

java.beans.PropertyChangeListener

java.awt.event.ActionListener

javax.swing.event.ChangeListener

我们可以看一下java.util.function包下大概可以分成4大类接口:消费型接口、供给型接口、函数型接口和断言型接口

接口 参数 返回值 类别
Consumer T void 消费型接口
Supplier None T 供给型接口
Function T R 函数型接口
Predicate T boolean 断言型接口

消费型接口源码

 /**
* 这个接口不像其他函数式接口,这个接口期待以副作用的形式参与操作
* 这里的副作用是指函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果
*/
@FunctionalInterface
public interface Consumer<T> { /**
* 本方法是传入lambda表达式覆盖的方法
*/
void accept(T t); /**
* 这个方法给出是融合多个Consumer,按顺序组合成一个系列的消费型接口
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

供给型接接口源码

     /**
* 看这个接口的定位,一般作为一个简单的工厂
*/
@FunctionalInterface
public interface Supplier<T> { /**
* 这里通过lambda表达式传入行为,注意没有参数
*/
T get();
}

函数型接口库

     @FunctionalInterface
public interface Function<T, R> { /**
* 这个是来接收lambda表达式的方法
*/
R apply(T t); /**
* 这个是在运行apply方法之前可以执行的方法
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
} /**
* 这个是在运行apply方法之后运行的方法
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
} /**
* 这个方法将返回输入,在stream里面,生成List或者Map的时候会用到
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}

断言型接口源代码

     @FunctionalInterface
public interface Predicate<T> { /**
* 通过lambda表达式来实现这个方法,注意lambda表达式要求一个参数,返回值为Boolean类型
*/
boolean test(T t); /**
* 和上面的test方法进行与操作
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
} /**
* test方法进行非操作
*/
default Predicate<T> negate() {
return (t) -> !test(t);
} /**
* 和test方法进行或操作
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} /**
* 注意,这里返回的不是Boolean,而是一个Predicate
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

在这里其实想补充一下断言型接口的那个静态方法的用法,第一次遇到这样的写法不太好理解

     public static void main(String[] args) {
/**
* 以下三种写法是等价的,第一种和第二种相比的优势在在于,多了一个isEqual可以传的参数,不会把程序写死,这里可以传递一个上下文的变量
* 而第三种呢,其实就是isEqual这个静态方法返回了一个已经被lambda表达式赋值的Predicate接口,所以可以直接调用test
*/
//第一种
String s = "test";
Predicate<String> p = Predicate.isEqual(s);
System.out.println(p.test("test"));
//第二种
Predicate<String> p2 = (null == "test") ? Objects::isNull : object -> "test".equals(object);
System.out.println(p2.test("test"));
//第三种
System.out.println(Predicate.isEqual("test").test("test"));
}

手把手的来几发lambda表达式练习(这里借鉴了http://www.importnew.com/16436.html点击传送门过去)

1、用lambda表达式实现Runnable接口

     public static void main(String[] args) {
//before java 8
new Thread(new Runnable(){ @Override
public void run() {
// TODO Auto-generated method stub
System.out.println("before java 8");
} }).start();
//after java 8
new Thread(()->System.out.println("after java 8")).start();
}

2、使用lambda表达式进行事件处理

     public static void main(String[] args) {
//before java 8
JButton jb = new JButton();
jb.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("before java 8");
} });
//after java 8
jb.addActionListener(e -> System.out.println("after java 8"));
}

3、使用lambda表达式进行迭代

     public static void main(String[] args) {
//before java 8
List<String> abc = Arrays.asList("abc", "def", "jhi");
for(String s : abc) {
System.out.println(s);
}
//after java 8
abc.forEach(System.out::println);
}

这里我们来看一下List::forEach源码

     /**
* forEach的参数是一个消费者接口,需要传入一个lambda表达式
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

我们来继续做一些复杂的操作(注意:这里其实还是用java7以前的东西,否则这里应该用流)

     public static void main(String[] args) {
List<String> abc = Arrays.asList("abc", "def", "jhi");
List<String> t = new ArrayList<String>();
abc.forEach(e -> {
if(e.contains("a")) {
t.add(e);
}
});
System.out.println(t);
}

4、使用lambda表达式和断言式接口

     public static void main(String[] args) {
List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp", "JS");
System.out.println("start with J");
filter(languages, s -> s.startsWith("J"));
System.out.println("start with J and contains a");
andFilter(languages, s -> s.startsWith("J"), s -> s.contains("a"));
System.out.println("start with J or contains a");
orFilter(languages, s -> s.startsWith("J"), s -> s.contains("a")); } public static void filter(List<String> name, java.util.function.Predicate<? super String> condition) {
name.stream().filter(condition).forEach(System.out::println);;
} public static void andFilter(List<String> name, Predicate<String> condition1, Predicate<String> condition2) {
name.stream().filter(condition1.and(condition2)).forEach(System.out::println);
} public static void orFilter(List<String> name, Predicate<String> condition1, Predicate<String> condition2) {
name.stream().filter(condition1.or(condition2)).forEach(System.out::println);
}

5、lambda表达式与map和reduce

     public static void main(String[] args) {
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
//注意到这里的reduce中的sum等于map后的第一个值,cost等于map后的第二个值,但是之后的行为是 ,将返回值赋给sum
//而map中继续传进来的值赋给cont
double d = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> {
System.out.println("sum=" + sum);
System.out.println("cost2=" + cost);
return sum + cost;
}).get();
System.out.println(d);
}

6、用lambda表达式写一个简单的 AOP

     public static void main(String[] args) {
AOP(x -> {
System.out.println("deal x");
return x;
},
x -> {
System.out.println("before deal x");
return x;
},
x -> {
System.out.println("after deal x");
return x;
});
} public static void AOP(Function<Object, Object> aim, Function<Object, Object> before, Function<Object, Object> after) {
aim.compose(before).andThen(after).apply(1);
}

总结

本文重要描述了lambda表达式的用法,但是并没有对其原理进行探究,不过还是继续说一些我的感受吧,lambda表达式看起来确实比从前的代码更简练,可读性更高一些,但是这都是建立在行为比较短暂的基础上,基本上三五行就描述清楚了,如果一个行为需要大量的语句去描述,我还是建议大家写成匿名内部类的形式,或者写一个类去实现接口,让自己的代码看起来更干净一些。第二部分介绍的几个函数式接口只是java.util.function中最基础的四个接口,其他接口基本上都是从这四个接口衍生出来的,大家可以自行阅读Bi开头的接口,就是变成了两个参数。第三部分举例子的时候不可比免的使用了stream,我们第三篇新特性的讲解,就会介绍stream

新特性2-lambda表达式的更多相关文章

  1. java8新特性: lambda表达式:直接获得某个list/array/对象里面的字段集合

    java8新特性: lambda表达式:直接获得某个list/array/对象里面的字段集合 比如,我有一张表: entity Category.java service CategoryServic ...

  2. Java8 新特性学习 Lambda表达式 和 Stream 用法案例

    Java8 新特性学习 Lambda表达式 和 Stream 用法案例 学习参考文章: https://www.cnblogs.com/coprince/p/8692972.html 1.使用lamb ...

  3. JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

    jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default   Lambda表达式     L ...

  4. Java8新特性之Lambda表达式

    lambda表达式是java8给我们带来的几个重量级新特性之一,借用lambda表达式,可以让我们的java程序设计更加简洁.最近新的项目摒弃了1.6的版本,全面基于java8进行开发,本文是java ...

  5. C++11新特性之一——Lambda表达式

    C++11新特性总结可以参考:http://www.cnblogs.com/pzhfei/archive/2013/03/02/CPP_new_feature.html#section_6.8 C++ ...

  6. Java8 新特性之Lambda表达式

    1. Lambda 表达式概述 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递); Lambda 表达式可以写出更简洁,更灵活的代码 ...

  7. jdk1.8新特性之lambda表达式

    lambda表达式其实就是指一个匿名函数,应用最广泛的就是匿名内部类的简化.在jdk1.8之前,我们定义一个匿名内部类可能需要写一大坨代码,现在有了lambda之后,可以写的很简洁了.但不是说lamb ...

  8. 夯实Java基础(二十二)——Java8新特性之Lambda表达式

    1.前言 Java 8于14年发布到现在已经有5年时间了,经过时间的磨练,毫无疑问,Java 8是继Java 5(发布于2004年)之后的又一个非常最重要的版本.因为Java 8里面出现了非常多新的特 ...

  9. 【Java8新特性】Lambda表达式基础语法,都在这儿了!!

    写在前面 前面积极响应读者的需求,写了两篇Java新特性的文章.有小伙伴留言说:感觉Lambda表达式很强大啊!一行代码就能够搞定那么多功能!我想学习下Lambda表达式的语法,可以吗?我的回答是:没 ...

  10. 【Java新特性】Lambda表达式典型案例,你想要的的都在这儿了!!

    写在前面 不得不说,有些小伙伴的学习热情真高,学完了Lambda表达式的语法,想来几个典型案例再强化下.于是问冰河能否给几个Lambda表达式的典型使用示例.于是乎,便有了这篇文章. 案例一 需求 调 ...

随机推荐

  1. twitter api的使用

    1.用手机号注册推特账号 https://twitter.com/ 2.进入网站 https://apps.twitter.com/ 创建第一个app,填入基本信息 name写完会检测是否已经存在像我 ...

  2. vue-cli目录结构介绍

    一个vue-cli的项目结构如下: 文件结构细分: 1.build——[webpack配置] build文件主要是webpack的配置,主要启动文件是dev-server.js,当我们输入npm ru ...

  3. Sublime如何设置背景透明

    Sublime如何设置背景透明 下载sublime 透明背景插件 我用的是git下载插件: git clone https://github.com/vhanla/SublimeTextTrans.g ...

  4. set集合 浅层拷贝会和深层拷贝

    一.什么是set集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 1.去重,把一个列表变成集合, ...

  5. java valueOf

    valueOf 方法可以将原生数值类型转化为对应的Number类型,java.lang.Number 基类包括ouble.Float.Byte.Short.Integer 以及 Long派生类, 也可 ...

  6. 【转】最全的 pip 使用指南,50% 你可能没用过

    [转]最全的 pip 使用指南,50% 你可能没用过 所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 P ...

  7. FOUC(Flash Of Unstyled Content)文档样式闪烁

    今天看面试题看到了这个新名词..我以前是没有发现过这种状况,应该是我一直都是将加载 CSS 的 link 标签写到 head 里的缘故吧. 什么是文档样式闪烁(Flash Of Unstyled Co ...

  8. mongotemplate 简单使用

    怎么说呢,工作需要,不可能给你慢慢学的时间,一切以先解决当前jira为前提, mondb 安装不说了网上一搜就有,推荐图形管理界面 robo3t 比较直观 1.多条件查询这个比较简单 有两种方法 1C ...

  9. uni-app学习资料整理-1.白话uni-app

    白话uni-app  https://ask.dcloud.net.cn/article/35657 文件内代码架构的变化 以前一个html大节点,里面有script和style节点: 现在templ ...

  10. 007-elasticsearch5.4.3【一】概述、Elasticsearch 访问方式、Elasticsearch 面向文档、常用概念

    一.概述 Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上. Elasticsearch 也是使用 Java 编写的,它的内部使用 L ...