(原)

以前,在创建泛型时,是这么写的:

List<String> list =  new  ArrayList<String>();

现在,可以这么写了:

List<String> list = new ArrayList<>();

在java8中,这种写法被叫作diamond语法,有些书里叫他钻石语法,有些则称之为菱形语法,说的就是这种语法。

看下面的例子:

package com.demo.jdk8;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function; public class Test3 {
public static void main(String[] args) { System.out.println("part1------------------");
List<String> list = Arrays.asList("a","b","hello"); list.stream().map(i -> i.toUpperCase()).forEach(i -> System.out.println(i)); System.out.println("part2--------------------"); list.stream().map(String::toUpperCase).forEach(i -> System.out.println(i)); System.out.println("part3--------------------"); Function<String , String> fun = String::toUpperCase;
System.out.println(fun.apply("hello")); System.out.println("part4-------------------");
System.out.println(calc1(3, v -> v+1)); System.out.println("part5------------------");
System.out.println(calc2(2, v -> v + 1, v -> v * 2)); System.out.println("part6------------------");
System.out.println(calc3(2, v -> v + 1, v -> v * 2)); System.out.println("part7------------------");
System.out.println(calc4(1,3,(v1,v2) -> v1 + v2)); System.out.println("part8------------------");
System.out.println(calc5(1,3,(v1,v2) -> v1 + v2 ,v -> v + 3) );
} public static Integer calc1(Integer a ,Function<Integer, Integer> fun){
return fun.apply(a);
} public static Integer calc2(int a ,Function<Integer, Integer> fun1,Function<Integer, Integer> fun2){
return fun1.compose(fun2).apply(a);
} public static Integer calc3(int a ,Function<Integer, Integer> fun1,Function<Integer, Integer> fun2){
return fun1.andThen(fun2).apply(a);
} public static Integer calc4(int a ,int b ,BiFunction<Integer, Integer, Integer> biFun){
return biFun.apply(a, b);
} public static Integer calc5(int a ,int b ,BiFunction<Integer, Integer, Integer> biFun,Function<Integer, Integer> fun){
return biFun.andThen(fun).apply(a, b);
}
}

Java8里面,part1、part2中List上层Collection接口中,加入了一个stream方法

/**
* Returns a sequential {@code Stream} with this collection as its source.
*
* <p>This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
* for details.)
*当分隔迭代器不能够返回一个不可变的,并发的或者延迟绑定值时,这个方法应该被重写。
* @implSpec
* The default implementation creates a sequential {@code Stream} from the
* collection's {@code Spliterator}.
*这个默认的实现会从Spliterator中创建一个串行流
* @return a sequential {@code Stream} over the elements in this collection
* @since 1.8
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
} /**
* Returns a possibly parallel {@code Stream} with this collection as its
* source. It is allowable for this method to return a sequential stream.
*
* <p>This method should be overridden when the {@link #spliterator()}
* method cannot return a spliterator that is {@code IMMUTABLE},
* {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
* for details.)
*
* @implSpec
* The default implementation creates a parallel {@code Stream} from the
* collection's {@code Spliterator}.
*
* @return a possibly parallel {@code Stream} over the elements in this
* collection
* @since 1.8
*/
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}

stream方法会返回一个串行流,parallelStream方法会返回一个并行流,在这个例子中,你可以将它看作返回了一个Stream,这个Stream是java8新加的接口,其中有一个map方法。

/**
* Returns a stream consisting of the results of applying the given
* function to the elements of this stream.
*返回一个流,其中包含将给定函数应用到该流的元素的结果。
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*这是一个中间操作
* @param <R> The element type of the new stream
* 返回一个新的流
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function to apply to each element
* @return the new stream
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

这里的map接收一个参数,是一个Function接口

@FunctionalInterface
public interface Function<T, R>
这是一个函数式接口,其中有一个抽象方法。
/**
* Applies this function to the given argument.
* 将这个参数运用到给定的参数上
* @param t the function argument
* @return the function result
*/
R apply(T t);

  这个方法的含义是接收一个流的参数,返回一个新的流。

在刚才的这个例子中list.stream().map(i -> i.toUpperCase()).forEach(i -> System.out.println(i));

list.stream()先将list转换成了stream,map是对Function接口的lambda实现,接收了每次迭代的一个参数,然后将该参数转换成大写再返回。

i -> i.toUpperCase()这里的i指的就是R apply(T t);这里的T,而i.toUpperCase()则是它的返回结果R,那String::toUpperCase这又是什么?在java8中,这叫方法的引用,因为toUpperCase并不是static方法,那么这个字符串必需是对象,那么这个对象是什么呢?其实这里的String就是传入的第一个参数T。这里总结一下,这个::后面的方法,一定是lambda表达式中第一个参数的方法。因为i -> i.toUpperCase()和String::toUpperCase是等价,这第二种写法里,你也可以认为有一个string对象调用了toUpperCase,这个对象是谁呢?就是第一种写法里面的i。(这里确实不太好理解)

在Function中,还有一个静态方法:

static <T> Function<T, T> identity() {
return t -> t;
}

这是在java8中引入的新的写法。接口中可以有方法的实现,可以是default的,也可以是static的。

例子中的part3部分,用方法引用的方式创建了一个Function的实例。

part4在calc方法中传递了二个参数,其中第二个参数实际上是传递了一个方法体,它其它可被称作为高阶函数,简单点讲就是可以把方法体当参数传入传出,这种方式在js中很常见。

另外,Function还有二个default函数。

/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*首先应用before这个函数返回一个组合函数的结果,然后再返回这个函数的结果。
*如果这个函数抛出异常,那么它会将这个异常交给调用这个函数的人处理。
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
} /**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*首先运用这个函数的输入返回一个组合函数,然后运用这个after的参数返回结果
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

 

这个二函数的文档描述的有些绕,不是很好解理,但是看了它的源码实现以后再去看文档,这个描述确实挺恰当的。

要解理这二个默认方法的意思,首先还是得搞清楚R apply(T t);输入一个参数,返回一个结果。

compose(Function<? super V, ? extends T> before) 这个方法里面,首先调用了before的apply方法,接收一个参数,返回一个结果,然后将返回的结果交给这个对象去执行apply方法,这个对象执行apply方法的参数就是这个before返回的结果,执行完后再返回一个结果。

andThen(Function<? super R, ? extends V> after) 这个方法恰好跟compose相反,首先执行这个对象的apply方法,然后把这个对象的结果交给after当作after的输入参数,它的返回值才是整个对象的结果。

结合上面demo的part5和part6部分,可以就能知道上面的结果是多少了。

Function是接收一个参数,返回一个结果,那有没有接收有二个参数,返回一个结果的呢?(JAVA只能返回一个结果!),当然有这个函数式接口叫BiFunction。

@FunctionalInterface
public interface BiFunction<T, U, R> { /**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u); /**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}

文档就不解释了,如果Function你懂了,那么BiFunction你应该也能明白,只是这个函数只有andThen方法,没有composed方法,这是为什么呢?

因为刚才说了,所有的函数都可以接收多个参数,但是返回结果只有一个,拿andThen来说,首先会运算BiFunction这个对象的apply方法,只是这个方法只会产生一个结果,那么这个结果只能拿来给它的参数Function<? super R, ? extends V> after当参数用了,这就是为什么BiFunction的andThen方法参数为Function 而不是BiFunction了。

没有composed也是有原因的,因为composed需要先执行参数的方法,再执行BiFunction里的apply方法,但是由于这个结果只有一个值 ,是无法满足BiFunction里apply需要二个参数的条件,所以这里就并没有像Function一样,有一个composed方法。

例子请看这里:https://github.com/LeeScofield/java8

Java 8 新特性:3-函数(Function)接口的更多相关文章

  1. Java 8 新特性1-函数式接口

    Java 8 新特性1-函数式接口 (原) Lambda表达式基本结构: (param1,param2,param3) -> {代码块} 例1: package com.demo.jdk8; i ...

  2. Java 8 新特性:1-函数式接口

    (原) Java 8 新特性1-函数式接口 Lambda表达式基本结构: (param1,param2,param3) -> {代码块} Lambda表达式结构: (type1 arg1,typ ...

  3. Java 8新特性-5 内建函数式接口

    在之前的一片博文 Lambda 表达式,提到过Java 8提供的函数式接口.在此文中,将介绍一下Java 8四个最基本的函数式接口 对于方法的引用,严格来讲都需要定义一个接口.不管我们如何操作实际上有 ...

  4. java8新特性学习:函数式接口

    本文概要 什么是函数式接口? 如何定义函数式接口? 常用的函数式接口 函数式接口语法注意事项 总结 1. 什么是函数式接口? 函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口 ...

  5. Java JDK1.8新特性之四大函数式接口

    JDK 1.8的一些新特性 四大核心函数式接口(Consumer.Predicate.Supplier.Function),结合lambda表达式 import java.util.ArrayList ...

  6. Java 8 新特性概述

    Oracle 在 2014 年 3 月发布了 Java 8 正式版,该版本是一个有重大改变的版本,对 JAVA 带来了诸多新特性.其中主要的新特性涵盖:函数式接口.Lambda 表达式.集合的流式操作 ...

  7. Java 8 新特性终极版

    声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:Java 8 特性 – 终极手册,我还是坚持自己 ...

  8. Java 8新特性前瞻

    快端午小长假了,要上线的项目差不多完结了,终于有时间可以坐下来写篇博客了. 这是篇对我看到的java 8新特性的一些总结,也是自己学习过程的总结. 几乎可以说java 8是目前为止,自2004年jav ...

  9. 【整理】Java 8新特性总结

    闲语: 相比于今年三月份才发布的Java 10 ,发布已久的Java 8 已经算是老版本了(传闻Java 11将于9月25日发布....).然而很多报道表明:Java 9 和JJava10不是 LTS ...

  10. Java 8 新特性终极指南

    1.前言 毫无疑问,Java 8的发布是自从Java5以来Java世界中最重大的事件,它在编译器.工具类和Java虚拟机等方面为Java语言带来的很多新特性.在本文中我们將一起关注下这些新变化,使用实 ...

随机推荐

  1. C-Linux_定时器示例使用

    #include <stdlib.h> #include <signal.h> ; static struct itimerval oldtv; struct itimerva ...

  2. C#设计模式之十八状态模式(State Pattern)【行为型】

    一.引言 今天我们开始讲“行为型”设计模式的第六个模式,该模式是[状态模式],英文名称是:State Pattern.无论是现实世界,还是面向对象的OO世界,里面都有一个东西,那就是对象.有对象当然就 ...

  3. mac node版本管理

    (0)简说 目前有n和nvm这两个工具可以对Node进行升级,以下简单介绍一下二者的使用. (1)n 安装很简单: $ sudo npm install -g n 另一种获取源码的方法安装: $ gi ...

  4. SAP MM PO 中的Delivery Date并非保存在EKPO表里

    采购订单行项目中的deliverydate并非如同其它字段值一样是保存在采购订单行项目表EKPO里的, 而是从EKET表里抓取最早的Delivery Date作为该Item的delivery date ...

  5. Dynamics AX 2012 性能优化之 SQL Server 复制

    Dynamics AX 2012 性能优化之 SQL Server 复制 分析数据滞后 在博文 Dynamics AX 2012 在BI分析中建立数据仓库的必要性 里,Reinhard 阐述了在 AX ...

  6. Jump Flood Algorithms for Centroidal Voronoi Tessellation

    Brief Implemented both CPU and GPU version, you could consider this as the basic playground to imple ...

  7. Android 彩色Toast实现

    Android默认的Toast太丑了,我们来封装一个花里胡哨的Toast吧,就叫ColoredToast. Github:https://github.com/imcloudfloating/Desi ...

  8. AndroBench手机性能测试

    AndroBench是一个基准测试应用程序,可以衡量你的Android设备的存储性能. AndroBench提供两种方式,第一种可以快速与其他设备的存储进行比较. 第二种 SQLite可以查询数据库表 ...

  9. Android-简单总结一下图片压缩

    最近项目需要用到图片压缩,所以简单总结一下.大致分为三种压缩. 图片质量压缩. 意思就是降低图片的质量,针对文件处理,但本身的像素点并不会减少. 本来像素点是这样的,经过算法计算,若一个像素点周围所存 ...

  10. go 利用orm简单实现接口分布式锁

    在开发中有些敏感接口,例如用户余额提现接口,需要考虑在并发情况下接口是否会发生问题.如果用户将自己的多条提现请求同时发送到服务器,代码能否扛得住呢?一旦没做锁,那么就真的会给用户多次提现,给公司带来损 ...