JAVA8学习总结——函数式编程与lambda表达式

       概述

       java8引入lambda表达式,提倡函数式编程。根据一些函数编程的定义指出,函数式编程是基于λ演算,而λ演算的一大特点就是以函数作为输入和输出。

         至于以函数作为输入和输出,在javaScript中体现得更明显一些,如下:


function function1(num1,num2){
	var reulst  = num1+num2;
	document.write(reulst);
}
			
function function2(fn){ //以函数fn作为输入
	return fn;  //以函数fn作为输出
}
var a  = function2(function1)
a(2,3);  //调用时才执行两个数值的计算和document.write;


再看一下java8引入的lambda表达式的写法:


public class LambdaTest {

	public static void main(String[] args) {
		LambdaTest lambdaTest = new LambdaTest();
		String result = lambdaTest.composeStr("hello world",(value) -> value+"--你好,世界!!");
		System.out.println(result);
	}
	
	public String composeStr(String str,Function<String,String> function){
	   	return function.apply(str);
	}
}


          其中( (value) -> value+"--你好,世界!!")这一段即为lambda表达式。这段代码看起来很像一个函数,以“->”作为分割符,左边是输入参数,右边是函数的执行体。但是与上面的javaScript代码又有一些不一样,javaScript中的输入参数直接就是函数本身,而java的函数输入则依赖于一个接口,即Function。也就是说,在javaScript中,函数和变量是同等地位,即:参数fn可以是一个函数,也可以是一个变量值;而在java中则不是,需要依托Function这个接口,也就是人们常说的执行环境。在java中,当把这里的Function换成其他的类型的参数时,就无法执行了。这是由以前java版本决定的,以前的java版本必须以变量作为输入和返回,为了向后兼容,则提出了Function这个接口。


          在上面的概述中,提到了两个重要的内容:1、lambda表达式的语法;2、Functioni接口

          Lambda表达式的语法(java):

                    (param1,param2,param3) -> {...} //参数列表 -> 执行体

          Lambda表达式是一种匿名的函数,没有访问修饰符,返回值声明。

          在上面的java代码中,没有出现花括号{}。这里因为还有两个概念:1、表达式(expression);2、语句(statement)。

          如果执行体只有一行 ,称之为表达式。表达式执行有一个特点,就是一定有返回值,跟(a > b)这类的表达式一样。所以,上面的代码中的Lambda表达式中,没有return关键字;在语句的结尾也没有分号(;)。

          如果是多行,称之为语句。语句是没有返回值的,所以当有多行执行体的时候,必须有花括号{}。如果要有返回值,还必须在需要返回位置加上return关键字。如:{return a+b;}

          如果Lambda表达式中的参数列表只有一个,则可以不用写括号。

          如果Lambda表达式中的参数列表没有参数,则必须要有(),括号中没有参数。

          注:参数前面是可以指定类型的,JDK有类型推断,可以自动解析参数的类型。如果遇到无法解析的情况,则需要我们手动指定类型。如:(String param1,String param2)。指定类型之后,必须使用括号括起来。


          Function接口:

          这个接口有三个比较重要的特点:

                    1、有@functionalInterface注解

                    2、有一个apply方法

                    3、有compose和andThen两个默认方法,且有方法体。(java8中,接口中的方法可以有方法体;但必须用default关键字修饰)。


          @FunctionalInterface接口是一种契约,被其修饰的接口称为函数式接口。这类接口有两个共同的特征:

                    1、有且只有一个方法,该方法接口一个参数,并返回一个参数。

                    2、覆写Object类的公用方法的方法不能算作是该接口的函数方法,也就是说覆写toString()之类的方法,不影响函数接口的定义(只能有一个方法)。

          至于为什么会有这种契约,主要是因为java语言是一种以变量作为输入和输出的语言。要引入Lambda表达式和函数式编程,又要向后兼容以前的JDK版本。这种约束都是特定的含义的:

          首先,接收一个参数,并返回一个结果。这个特征是java中方法的典型特征。

          其次,有且只有一个方法。函数式编程强调以函数作为输入和输出,但是java语言并不具备这个能力。所以通过在接口中定义一个方法,且限制它只能有一个方法,这样就能将外部传入的方法体放入这个仅有的方法里面。这样做,既没有破坏java其他的语言特性,同时又能满足函数式编程的特征。这也就是上面说的,在java中,函数与变量并不是同等地位。java中的函数式编程需要依托Function这样的执行环境。

          再者,覆写Object类的公用方法,不算是接口默认的方法。在java中,Object是所有类的父类,所有类总都会有Object的公用方法,自然在函数式接口中也不会列外。所以覆写这些方法并不会影响接口中的有且仅有一个方法的特性。

          最后,对于被@FunctionInterface修饰的接口,称为函数式接口。没有被改注解修饰,且满足上面的两个特征的接口也算作是函数式接口。也就是说,在java8中,这种特征的接口被当成了一种默认的方式。这也就是为什么java8提倡函数式编程的原因吧。

         在JDK8中有很多这种接口,如:Predicate,Consumer  等等。


          函数式编程与Lambda表达式

          函数式编程有三个重要的特点:

                    1、惰性计算。

                    2、以函数作为输入和输出,传递的是行为 ,而不是值

                    3、引用透明,无状态。

                    4、高度抽象化。

先看一段代码:


public class NumberFunction {

    public Integer calculateInteger(Integer number, Function<Integer,Integer> function){
        return function.apply(number);
    }
}


public class CalculateTest01 {

    public static void main(String[] args) {
        NumberFunction numberFunction = new NumberFunction();
        System.out.println(numberFunction.calculateInteger(3,value -> value +1));
    }
}


public class CalculateTest02 {

    public static void main(String[] args) {
        NumberFunction numberFunction = new NumberFunction();
        System.out.println(numberFunction.calculateInteger(3,value -> value -1));
    }
}


          在上面的代码中,同一个类的同一个方法,因为传入的执行方式不一样而得出不一样的结果。一个加1,另一个则是减1.这也就体现了函数编程的两个特征(传递的是行为,而不是值和高度的抽象化)。换做以前的java代码,需要为“+”和“-”分别定义不同的方法。用两句话概括一下这里的抽象化:

                    1、calculateInteger的定义是:我需要对这个传入参数进行计算,怎么计算,由您决定。

                    2、如果单独做一个加法的方法,它的定义是:我需要对这个传入的值进行“加1”操作,你无法改变我的行为。

          很明显,函数式编程的做法显得更优雅一些,同时也可以看出,抽象程度更高。

          只有在方法被调用的时候,才进行值得运算,这也就是惰性计算。因为是惰性计算,调用时才进行计算,可以得到多种可能性的输出。而且,整个过程都是无状态的参数传递,没有保留任何的状态。正式由于这种无状态的透明引用,是的在上面的惰性计算中不会产生副作用。就像多线程编程中,都是讲变量声明在方法内部,方法结束即销毁,不保留状态,是影响范围变小。


以上仅为本人学习总结,如有不当之处,还望指正,谢谢!!!            


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值