Java8-函数式编程

Java8-函数式编程

为什么java8 的Lambda 这么受关注?

Java8可以用Lambda表达式很简便的写出复杂的处理集合的逻辑。Lambda 可以理解为一种匿名函数的代替。使用它可以简化代码,提高开发效率。

函数编程演化历程

函数编程演化历程

  • 1.将业务逻辑直接写死在代码里。
  • 2.将单一维度的条件做为参数传入方法中。方法内根据参数进行业务逻辑实现。
  • 3.将多个维度的条件做为参数传入方法中。业务实现需要根据不同的参数处理不同逻辑。
  • 4.将业务逻辑封装为一个实体类,方法接受实体类为参数,方法内部调用实体类的处理逻辑。
  • 5.调用方法时不在创建实体类,而是使用匿名函数的形式替代。
  • 6.使用Lambda表达式替代匿名函数的形式,做为方法的参数。真正实现判断逻辑参数化传递。

函数编程

函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。

命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。

函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

Lambda表达式

Lambda表达式的两种形式

  • (parameters) -> expression

  • (parameters) ->{ statements; }
    以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

表达式实例

// 1. 不需要参数
() -> 5 ;
() -> System.out.println("hello world");
  
// 2. 接收一个参数
x -> 2 * x ;
() -> System.out.println("hello world");

// 3. 没有参数逻辑复杂
() -> {
    System.out.println("hello world1");
    System.out.println("hello world2");
}
// 3. 接受2个参数(数字)
BinaryOperator<Long> functionAdd = (x,y)-> x + y ;
Long result = functionAdd(1L,2L);
  
// 4. 接收2个int型整数,显示声明参数类型
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

以上就是lambda的介绍。

函数式接口介绍

通常Lambda表达式是用在函数式接口上使用的。从Java8开始引入了函数式接口,其说明比较简单:函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。 java8引入@FunctionalInterface 注解声明该接口是一个函数式接口。

语法定义

@FunctionalInterface
public interface ICollectionService {
    void test();
}

常用的函数式接口

接口 参数 返回类型 描述
Predicate<T> T boolean 用于判别一个对象
Consumer<T> T void 用于接收一个对象进行处理但没有返回
Function<T,R> T R 转换一个对象为不同类型的对象
Supplier<T> None T 提供一个对象
UnaryOperator<T> T T 接收对象并返回同类型的对象
BinaryOperator<T> (T,T) T 接收两个同类型的对象,并返回一个原类型的对象

Predicate

java.util.function.Predicate<T> 接口定义了一个名叫 test 的抽象方法,它接受泛型 T 对象,并返回一个boolean值。在对类型 T进行断言判断时,可以使用这个接口。通常称为断言性接口。
例如

@FunctionalInterface
public interface PredicateTest<T> {
    boolean test(T t);
}

@SpringBootTest
class LambdaTestApplicationTests {

    //借助Lambda 表达式实现Predicate test方法
    @Test
    public void test3(){
        PredicateTest<String> predicateTest = (str)->str.isEmpty()||str.trim().isEmpty();
        System.out.println(predicateTest.test(""));
        System.out.println(predicateTest.test(" "));
        System.out.println(predicateTest.test(" as"));
    }
}

Consumer

java.util.function.Consumer<T>接口定义了一个名叫 accept 的抽象方法,它接受泛型T,没有返回值(void)。如果需要访问类型 T 的对象,并对其执行某些操作,可以使用这个接口,通常称为消费性接口。

    @FunctionalInterface
    public interface ConsumerTest<T> {
        void accept(T t);
    }

  /**
     * 借助Lambda表达式实现Consumer accept方法
     */
    @Test
    public void test4(){
        ConsumerTest<Collection> consumerTest = collection ->{
            if (!CollectionUtils.isEmpty(collection)){
                for (Object o : collection) {
                    System.out.println(o);
                }
            }
        };

        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");

        consumerTest.accept(list);
    }

Function

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果需要定义一个Lambda,将输入的信息映射到输出,可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度),通常称为功能性接口。

    @FunctionalInterface
    public interface FunctionTest<T,R> {
        R apply(T t);
    }

    @Test
    public void test5() {
        FunctionTest<String, String> functionTest = password -> Base64.getEncoder().encodeToString(password.getBytes());
        System.out.println(functionTest.apply("123456"));
    }

Supplier

java.util.function.Supplier<T>接口定义了一个get的抽象方法,它没有参数,返回一个泛型T的对象,这类似于一个工厂方法,通常称为功能性接口。

    @FunctionalInterface
    public interface SupplierTest<T> {
        T get();
    }

    @Test
    public void test6(){
        int[] arr = {100,0,-50,880,99,33,-30};
        SupplierTest<Integer> s = ()->{
            int max = arr[0];
            //遍历数组,获取数组中的其他元素
            for (int i : arr) {
                //使用其他的元素和最大值比较
                if(i>max){
                    //如果i大于max,则替换max作为最大值
                    max = i;
                }
            }
            //返回最大值
            return max;
        };
       System.out.println(s.get().intValue());
    }

UnaryOperator

java.util.function.UnaryOperator<T> 定义了一个identity方法.
这个接口,只接收一个泛型参数T,集成Function接口,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;这个接口定义了一个静态方法,返回泛型对象的本身;

    @Test
    public void test7() {
        UnaryOperator<Integer> test = x -> x + 100;
        System.out.println(test.apply(100));
    }

BinaryOperator

java.util.function.BinaryOperator<T>接口用于执行lambda表达式并返回一个T类型的返回值。

    /**
     * 接收两个同类型的对象,并返回一个原类型的对象
     */
    @Test
    public void test8(){
        BinaryOperator<Integer> add = (n1, n2) -> n1 + n2;
        //apply方法用于接收参数,并返回BinaryOperator中的Integer类型
        System.out.println(add.apply(3, 4));
    }

BiConsumer

这个接口接收两个泛型参数,跟Consumer一样,都有一个 accept方法,只不过,这里的,接收两个泛型参数,对这两个参数做下消费处理;使用这个函数式接口的终端操作有map的遍历;下面看下面的例子,两个参数消费数据的.可以看到,Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap 的数据进行消费;BiConsumer函数接口还有一个默认函数,andThen,接收一个BiConsumer接口,先执行本接口的,再执行传入的参数。

Map<String, String> map = new HashMap<>();
        map.put("a", "a");
        map.put("b", "b");
        map.put("c", "c");
        map.put("d", "d");
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });

方法引用

有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。 调用特定方法的lambda表达式的一种快捷写法,可以让你重复使用现有的方法定义,并像lambda表达式一样传递他们。

语法:
方法归属者::方法名
静态方法的归属者为类名,普通方法归属者为对象

指向静态方法的方法引用

public void test(){
     Consumer<String> consumer = (String s) -> Integer.parseInt(s);
        Consumer<String> consumer1 = Integer::parseInt;
}

指向对象的实例方法引用

StringBuilder stringBuilder = new StringBuilder();
  Consumer<String> consumer = (String s) -> stringBuilder.append(s);
  Consumer<String> consumer1 = stringBuilder::append;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容