jdk8中函数表达式lambda及其相关的新特性

lambda表达式的概念

使用->表达我的我们叫做lambda表达式,实现了指定接口,并返回接口对象的一种写法。

public class ThreadDemo {

    public static void main(String[] args) {
        Object target = new Runnable() {

            @Override
            public void run() {
                System.out.println("ok");
            }
        };
        new Thread((Runnable) target).start();

        // jdk8 lambda
        Object target2 = (Runnable)() -> System.out.println("ok");
        Runnable target3 = () -> System.out.println("ok");
        System.out.println(target2 == target3); // false
        
        new Thread((Runnable) target2).start();
    }

}

对于使用lambda表达式的接口的要求就是接口中必须只有一个接口方法。这也是引导我们写接口的时候尽量少写接口,尽量遵守单一责任制。jdk8中引入了一个新的名称叫函数接口,使用@FunctionalInterface标注,作用同@override一样是编译器的校验作用。

默认方法,或者叫默认实现方法,我们可以通过lambda表达式得到一个接口实例然后就可以对用这个默认方法,我们可以把默认方法当做类里面的方法来理解。

@FunctionalInterface
interface Interface1 {
    int doubleNum(int i);

    default int add(int x, int y) {
        return x + y;
    }

    static int sub(int x, int y) {
        return x - y;
    }
}


    public static void main(String[] args) {
        Interface1 i1 = (i) -> i * 2;

        Interface1.sub(10, 3);
        System.out.println(i1.add(3, 7));
        System.out.println(i1.doubleNum(20));
}

jdk8中提供的函数接口

首先实现一个需求对数字进行格式,化格式化成钱的格式 即“#,###” 我们使用如下代码来实现

import java.text.DecimalFormat;

/**
 * @author 杨红星
 * @version 1.0.0
 * @date 2018/7/29
 */
interface IMoneyFormate {
    String formate(int money);
}

class MyMoney1 {
    private final int money;
    public MyMoney1(int money) {
        this.money = money;
    }
    public void printMoney(IMoneyFormate moneyFormat) {
        System.out.println("我的存款:" + moneyFormat.formate(this.money));
    }
}

public class MoneyDemoOld {
    public static void main(String[] args) {
        MyMoney1 me = new MyMoney1(99999999);
        me.printMoney( i -> new DecimalFormat("#,###")
                .format(i));
    }
}

我们查看代码可以发现其实这个接口IMoneyFormate没有多少必要,事实上我们可以认为这个接口的作用就是告诉我们参数类型返回值类型,所以可以去掉这个接口同时使用jdk8中的新特性。使用Function<Integer, String>来表明方法接受的是一个接口类型,同时指定参数和返回值类型分别是intger和string,调用apply方法。最后一个同时使用了新特性中函数接口链式操作 下面看代码:

class MyMoney {
    private final int money;
    public MyMoney(int money) {
        this.money = money;
    }
    public void printMoney(Function<Integer, String> moneyFormat) {
        System.out.println("我的存款:" + moneyFormat.apply(this.money));
    }
}

public class MoneyDemo {
    public static void main(String[] args) {
        MyMoney me = new MyMoney(99999999);
        Function<Integer, String> moneyFormat = i -> new DecimalFormat("#,###")
                .format(i);
        // 函数接口链式操作
        me.printMoney(moneyFormat.andThen(s -> "人民币 " + s));
    }
}

新增特性默认接口方法 默认方法可以就当做类里面的方法 不仅有默认放还可以有静态方法 看代码

@FunctionalInterface
interface Interface1 {
    int doubleNum(int i);

    default int add(int x, int y) {
        return x + y;
    }

    static int sub(int x, int y) {
        return x - y;
    }
}
public strictfp class LambdaDemo1 {

    public static void main(String[] args) {
        Interface1 i1 = (i) -> i * 2;

        Interface1.sub(10, 3);  //7
        System.out.println(i1.add(3, 7));   //10
        System.out.println(i1.doubleNum(20));  //40

        // 这种是最常见写法
        Interface1 i2 = i -> i * 2;

        Interface1 i3 = (int i) -> i * 2;

        Interface1 i4 = (int i) -> {
            System.out.println("-----");
            return i * 2;
        };

    }

}

断言函数和消费者函数

public class FunctionDemo {

    
    public static void main(String[] args) {
        // 断言函数接口
        IntPredicate predicate = i -> i > 0;
        System.out.println(predicate.test(-9));
        
        //
        // IntConsumer

        // 消费函数接口
        Consumer<String> consumer = s -> System.out.println(s);
        consumer.accept("输入的数据");
    }

}

方法引用

Consumer<String> consumer = s -> System.out.println(s);

当你的方法的参数和返回值的类型一样我们就可以缩写 去掉s用 ::代替即

Consumer<String> consumer=System.out::println

方法引用的种类
1、静态方法的引用
使用方法类名加方法名 类名::方法名
2、非静态方法 非静态方法的实例::方法名
非静态方法也可以使用类名加方法名的形式,印为jdk会为每一方法传值时第一个参数为this 如

public void test(String s){
    
}

编译的时候变成

//Demo为类名
public void test(Demo this,String s){
    
}

所以在非静态方法中使用类名加方法名的方式为

BigFunction <类名(实例的类型),输入参数类型,输出类型>=类名::new

3、构造函数的方法引用
不含参数

function<参数类型,类名(实例的类型,返回值的类型)> 函数名称=  类::new

含参数的

function<参数类型,类名(实例的类型,返回值的类型)> 函数名称=类名::new
package lambda;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;

class Dog {
    private String name = "哮天犬";

    /**
     * 默认10斤狗粮
     */
    private int food = 10;

    public Dog() {

    }

    /**
     * 带参数的构造函数
     * 
     * @param name
     */
    public Dog(String name) {
        this.name = name;
    }

    /**
     * 狗叫,静态方法
     * 
     * @param dog
     */
    public static void bark(Dog dog) {
        System.out.println(dog + "叫了");
    }

    /**
     * 吃狗粮 JDK
     * 
     * 默认会把当前实例传入到非静态方法,参数名为this,位置是第一个;
     * 
     * @param num
     * @return 还剩下多少斤
     */
    public int eat(int num) {
        System.out.println("吃了" + num + "斤狗粮");
        this.food -= num;
        return this.food;
    }

    @Override
    public String toString() {
        return this.name;
    }
}

public class MethodRefrenceDemo {

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(3);

        // 方法引用
        Consumer<String> consumer = System.out::println;
        consumer.accept("接受的数据");

        // 静态方法的方法引用
        Consumer<Dog> consumer2 = Dog::bark;
        consumer2.accept(dog);

        // 非静态方法,使用对象实例的方法引用
        // Function<Integer, Integer> function = dog::eat;
        // UnaryOperator<Integer> function = dog::eat;
        IntUnaryOperator function = dog::eat;
        
        // dog置空,不影响下面的函数执行,因为java 参数是传值
        dog = null;
        System.out.println("还剩下" + function.applyAsInt(2) + "斤");
        //
        // // 使用类名来方法引用
        // BiFunction<Dog, Integer, Integer> eatFunction = Dog::eat;
        // System.out.println("还剩下" + eatFunction.apply(dog, 2) + "斤");
        //
        // // 构造函数的方法引用
        // Supplier<Dog> supplier = Dog::new;
        // System.out.println("创建了新对象:" + supplier.get());
        //
        // // 带参数的构造函数的方法引用
        // Function<String, Dog> function2 = Dog::new;
        // System.out.println("创建了新对象:" + function2.apply("旺财"));

    }

    private static void test(List<String> list) {
        list = null;
    }
}

lambda表达式的类型推断

package lambda;

@FunctionalInterface
interface IMath {
    int add(int x, int y);
}

@FunctionalInterface
interface IMath2 {
    int sub(int x, int y);
}


public class TypeDemo {

    public static void main(String[] args) {
        // 变量类型定义
        IMath lambda = (x, y) -> x + y;

        // 数组里
        IMath[] lambdas = { (x, y) -> x + y };

        // 强转
        Object lambda2 = (IMath) (x, y) -> x + y;
        
        // 通过返回类型
        IMath createLambda = createLambda();
        
        TypeDemo demo = new TypeDemo();
        // 当有二义性的时候,使用强转对应的接口解决
        demo.test( (IMath2)(x, y) -> x + y);
    }
    
    public void test(IMath math) {
        
    }
    
    public void test(IMath2 math) {
        
    }
    
    public static IMath createLambda() {
        return  (x, y) -> x + y;
    }

}

lambda表达式的变量引用

插曲在jdk8之前内部类引用外部类这个类必须是final类型,在jdk8中这个final是可以省略的,但是依然是final的

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 变量引用
 */
public class VarDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Consumer<String> consumer = s -> System.out.println(s + list);
        consumer.accept("1211");
    }

}

联级表达式和柯里化
联级表达式就是x->y->x+y;这种形式
完整的写出来意思就清楚了

Function<Integer,Function<Integer,Integer>>=x->y->x+y;

就是说y是一个函数然后作为x的返回函数,y又是x+y的返回函数
所谓柯里化就是多个参数变成一个参数看代码

import java.util.function.Function;

/**
 * 级联表达式和柯里化 
 * 柯里化:把多个参数的函数转换为只有一个参数的函数 
 * 柯里化的目的:函数标准化
 * 高阶函数:就是返回函数的函数
 */
public class CurryDemo {

    public static void main(String[] args) {
        // 实现了x+y的级联表达式
        Function<Integer, Function<Integer, Integer>> fun = x -> y -> x
                + y;
        System.out.println(fun.apply(2).apply(3));

        Function<Integer, Function<Integer, Function<Integer, Integer>>> fun2 = x -> y -> z -> x
                + y + z;
        System.out.println(fun2.apply(2).apply(3).apply(4));

        int[] nums = { 2, 3, 4 };
        Function f = fun2;
        
        for (int i = 0; i < nums.length; i++) {
            if (f instanceof Function) {
                Object obj = f.apply(nums[i]);
                if (obj instanceof Function) {
                    f = (Function) obj;
                } else {
                    System.out.println("调用结束:结果为" + obj);
                }
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容