不论是C语言,Java,仍是数据库的SQL,函数Function绝对是咱们最常打交道的对象。今天要介绍的“函数式编程”,是指Java8引入的新语法,虽然Java8在2014年就推出了,我想应该仍是有不少同窗不太了解。
咱们之前接触的函数,基本都是接受变量参数,返回结果参数。这些参数能够是基本变量、对象和数组等,但能不能是一段代码块呢?Java里面代码块都能抽象成一个函数,所谓函数式编程,就是在定义一个函数时,它的参数也是一个函数的接口。在调用函数时,再去实现这个函数的接口,这个过程,也就出现了lambda表达式。java
以下文,咱们定义了一个函数 - 通知全部员工。数据库
/** * 通知员工 * @param notify */ public static void notifyEmployees( Consumer<Employee> notify){ List<Employee> employeeList=employeeMapper.getAllEmployees(); for(Employee employee : employeeList){ notify.accept(employee); } }
可是咱们没有定义怎么去通知员工,多是邮件通知,多是短信通知,也有多是微信通知,等等。咱们将具体通知的方式,做为这个函数的一个传入参数。这里借助了Java自带的函数式接口 Consumer<T> ,后面会介绍它怎么用。那么我在调用这个方法的时候,就能够根据状况去实现这个函数式接口。编程
//经过邮件 notifyEmployees((employee)->{ sendEmail(employee.getEmail()); }); //经过短信 notifyEmployees((employee)->{ sendSms(employee.getPhoneNumber()); }); // 等等 ...
Lambda表达式支持将代码块做为方法参数,Lambda 表达式容许使用更简洁的代码来建立只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
在上文实现函数式编程时,你或许感受奇怪,为何经过 -> 箭头之类的语法,就能实现接口?这就是Lambda表达式的语法,实际上是对咱们之前建立匿名内部类的简写。api
匿名内部类也是一种简化代码的方式,我最先接触到的匿名内部类,就是在多线程中建立Thread对象时,经过实现Runnable接口来建立。数组
Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始了!"); } }); thread.start();
实际上new Thread(Runnable target) 接收的参数,是实现了Runnable接口的类的对象。但因为咱们关心的是接口里面的实现方法,而不是类的名字,因此用匿名内部类的方式来建立对象。
可是在看了Runnable接口的定义后,你会发现这个类只有一个抽象方法,这就又让人很不爽。我关心的只是这个类里面的这一个抽象方法,能不能不去实现这个类?Lambda表达式就应运而生了,它让代码看起来更简洁美观。服务器
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName() + "开始了!")); thread.start();
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,可是能够有多个非抽象方法的接口。函数式接口能够被隐式转换为 lambda 表达式。
咱们再看一下Runnable这个函数式接口。微信
@FunctionalInterface public interface Runnable { public abstract void run(); }
函数式接口的特色以下:多线程
Java8定义了大量的预约义函数式接口,用于常见类型的代码传递,这些函数定义在java.util.function下。app
接受参数对象T,返回一个boolean结果dom
Predicate<String> isAdminPre=(String username)-> "admin".equals(username); System.out.println(isAdminPre.test("kerry")); System.out.println(isAdminPre.test("admin"));
接收参数对象T,不返回结果
Consumer<String> msgCon = (String msg) -> { System.out.println("发送消息" + msg); System.out.println("消息发送完成"); }; msgCon.accept("01!01!我是02!");
接收参数对象T,返回结果对象R
Function<Date,String> timeFunc=(Date date)->{ Calendar calendar=Calendar.getInstance(); calendar.setTime(date); int hour=calendar.get(Calendar.HOUR_OF_DAY); if(hour>=6 && hour<12){ return "上午"; }else if(hour>=12&& hour<18){ return "下午"; }else { return "晚上"; } }; System.out.println(timeFunc.apply(new Date()));
不接受参数,提供对象T的建立工程
Supplier<String> uuidSup=()-> UUID.randomUUID().toString().replace("-",""); System.out.println(uuidSup.get());
接收参数对象T,返回结果对象T
UnaryOperator<String> trimUO=(String str)->{ if(str ==null){ return str; } return str.trim(); }; System.out.println(trimUO.apply(" Hello "));
接收两个T对象,返回一个T对象结果
BinaryOperator<Integer> maxBO=(Integer i1,Integer i2)-> i1>i2?i1:i2; System.out.println(maxBO.apply(13,20));
Java 8 API添加了一个新的抽象称为流Stream,可让你以一种声明的方式处理数据。Stream API 借助于Lambda 表达式,极大的提升编程效率和程序可读性。
Stream 使用一种相似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象,它专一于对集合对象进行各类很是便利、高效的聚合操做,或者大批量数据操做。这种风格将要处理的元素集合看做一种流, 流在管道中传输, 而且能够在管道的节点上进行处理, 好比筛选, 排序,聚合等。
元素流在管道中通过中间操做(intermediate operation)的处理,最后由最终操做(terminal operation)获得前面处理的结果。
我我的感受,Stream有点像是用Java代码编写的Hadoop。Hadoop利用分布式服务器,提升并行计算的能力,而Stream也一样能够经过多线程,提升数据处理的能力。它们也一样用map和reduce,作数据的映射和整合。
流的操做类型分为两种:
常见的流操做方法有以下:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
关于Stream的知识点比较多,《Java 8 中的 Streams API 详解》 这篇文章是我看到比较好的Stream系列博客,建议你们直接看这篇文章。
Optional有点像Stream,是Java8推出的处理数据的包装类,不少方法的实现都和lambda表达式结合在一块儿,推近了函数式编程风格。Optional主要用来处理常见的空指针异常,学会它,你的代码会美观不少。
可经过 Optional.ofNullable(T value)和Optional.of(T value),前者容许被包装的对象为空。然后者,若是被包装的对象为空仍是会报空指针异常。
从Optional实例中取回实际值对象的方法之一是使用 get()方法,不过这个方法会在值为null的时候抛出异常。要避免异常,你能够选择先用isPresent()验证是否有值。
User user=new User(); Optional<User> userOptional=Optional.ofNullable(user); //或 Optional<User> userOptional=Optional.of(user);
Optional对象的isPresent()方法返回boolean值,若是为空返回false,不为空返回true。
该方法除了执行检查,还接受一个Consumer(消费者) 参数,若是对象不是空的,就对执行传入的 Lambda 表达式.
orElse(),若是有值则返回该值,不然返回传递给它的参数值。
orElseGet(),这个方法会在有值的时候返回值,若是没有值,它会执行做为参数传入的 Supplier 函数式接口,并将返回其执行结果。
User newUser=Optional.ofNullable(user).orElse(new User("anonymous")); //或 User newUser=Optional.ofNullable(user).orElseGet(()->{ User sUser=new User("anonymous"); sUser.setAge(25); return sUser; });
值得注意的是,就算对象不为空,orElse() 方法仍然会建立User对象,只是不会给newUser赋值。而orElseGet() 方法,只有当对象为空的时候才会建立 User 对象。这个差别,在方法调用量大的时候,对性能产生的影响差别比较大。
orElseThrow() 会在对象为空的时候抛出异常,而不是返回默认值,这个方法让咱们有更丰富的语义,能够决定抛出什么样的异常,而不老是抛出 NullPointerException。
map() 对值应用(调用)做为参数的函数,而后将返回的值包装在Optional中。这就使对返回值进行链试调用的操做成为可能 。
flatMap() 也须要函数做为参数,并对值调用这个函数,可是会直接返回结果,并不会包装在Optional中。
User user=new User(); String name=Optional.ofNullable(user) .map(User::getName) .orElse("anonymous"); System.out.println(name);
map经常使用于链式方法判断中,例如上文代码实际上等于:
User user=new User(); String name="anonymous"; if(user!=null){ if(user.getName()!=null){ name=user.getName(); } } System.out.println(name);
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。若是测试结果为 false,会返回一个空的 Optional。
User user=new User("Kerry"); user.setEmail("kerry.wu@definesys.com"); Optional<User> filterUser=Optional.ofNullable(user) .filter(u->u.getEmail()!=null&&u.getEmail().contains("@"));