最近几天学习了一下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. 3D Computer Grapihcs Using OpenGL - 03 OpenGL Buffer Data

    本节绘制一个三角形,并讲解Buffer Object-缓冲对象 OpenGL的窗口坐标 屏幕中心为坐标原点,横向朝右为x正方向,纵向朝上为y正方向,最大值最小值分别为1,-1. Buffer Obje ...

  2. Cmdow-一个win32窗口管理命令行工具

    最近有个需求,将同一个程序运行8个实例,并按照规则在两个窗口上分布,本以为用bat就可以实现,结果发现没那么容易,搜了很久找到了这个工具cmdow.exe,发现这个东西真不错. 符合了我们项目的需求: ...

  3. 170826-关于spring的知识点及练习

    1.Spring作用: 1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!] 2.将其他组件粘合在一起 3.IOC容器和AOP[Aspect ...

  4. Vim 编辑器学习笔记

    参考资料: 世界上最牛的编辑器: Vim 1

  5. postgresql批量删除表

    CREATE FUNCTION del_ora_table() RETURNS void AS $$ DECLARE tmp ); DECLARE names CURSOR FOR select ta ...

  6. Flashtext:大规模数据清洗的利器

    Flashtext:大规模数据清洗的利器 在这篇文章中,我们将介绍一种新的关键字搜索和替换的算法:Flashtext 算法.Flashtext 算法是一个高效的字符搜索和替换算法.该算法的时间复杂度不 ...

  7. Ueditor1.4.4 Jsp版本视频上传成功,重新编辑时无法打开、在文本框内无法显示、html源码显示src为空

    1. 编辑 ueditor.config.js 第355行 将 whitList 改为 whiteList 2.编辑ueditor.all.js 注释掉7343.7344.7345行代码,即: var ...

  8. lazyload懒加载插件

    在main.js中引入vue-lazyload插件  并使用 注册插件: import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyLoad,{ lo ...

  9. 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第4节 ArrayList集合_12-对象数组

    对象数组是怎么回事呢? 新建Person类 代码生成后续的代码 生成一个无参构造 两个成员变量都选上,这是全参构造 生成getter和setter 数组的默认的第几0个元素是null 创建三个对象 输 ...

  10. stack() unstack()函数

    总结: 1.stack:  将数据的列索引转换为行索引 2.unstack:将数据的行索引转换为列索引 3.stack和unstack默认操作为最内层,可以用level参数指定操作层. 4.stack ...