函数式编程,lambda相关

lambda,很基础的技术,这里做下回顾,查漏补缺。


函数式接口

写法,一般2用的比较多:

@FunctionalInterface
interface Interface1{
    int countNum(int i);
}

public class LambdaDemo {
    public static void main(String[] args) {

        Interface1 i1 = (i) -> i * 2;

        Interface1 i2 = i -> i * 2;

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

        Interface1 i4 = (int i) -> {
            return i * 2;
        };
    }
}

当然,接口里面的方法只能有一个,如果多的话,也就不知道要实现哪一个了。

注解@FunctionalInterface,更多的是编译器的校验,不加也可以,表示是函数式接口,如果写多个接口会报错,所以能加就加。

而如果是下一个情况:

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

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

jdk8新增了带默认实现的方法,这个情况也是不会报错的。

看下面一个例子:

@FunctionalInterface
interface IMoneyFormat{
    String format(int i);
}
class MyMoney{
    private final int money;

    public MyMoney(int money) {
        this.money = money;
    }

    public void printMoney(IMoneyFormat moneyFormat){
        System.out.println("存款:" + moneyFormat.format(this.money));
    }
}
public class MoneyDemo {
    public static void main(String[] args) {
        MyMoney my = new MyMoney(9999999);
        my.printMoney(i -> new DecimalFormat("#,###").format(i));
    }
}

结果:

从上面可以知道,lambda不关心接口名字,实现方法是什么,只需要知道输入输出是什么格式就可以了。

所以我们可以不用写上面的接口,改造下:

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 my = new MyMoney(9999999);
        my.printMoney(i -> new DecimalFormat("#,###").format(i));
    }
}

源码:

T:输入,R:输出。

所以我们用函数接口,就可以不用定义那么多接口了,这里再用andThen做加工:

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 my = new MyMoney(9999999);
        Function<Integer , String> moneyFormat = i -> new DecimalFormat("#,###").format(i);
        my.printMoney(moneyFormat.andThen(s -> "人民币" + s));
    }
}

结果:


接下来看下jdk8自带的函数式接口:

接口输入参数返回类型说明
Predicate<T>Tboolean断言
Consumer<T>T/消费一个数据
Function<T,R>TR输入T,输出R的函数
Supplier<T>/T提供一个数据
UnaryOperator<T>TT一元函数(输入输出类型相同)
BiFunction<T,U,R>(T,U)R2个输入的函数
BinaryOperator<T>(T,T)T二元函数(输入输出类型相同)

举几个例子:

public class FunctionDemo {
    public static void main(String[] args) {
        //断言
        Predicate<Integer> predicate = i -> i > 0;
        System.out.println(predicate.test(-9));

        //消费函数
        Consumer<String> consumer = s -> System.out.println(s);
        consumer.accept("耗子肉");

        //服务函数
        Supplier<String> supplier = () -> "耗子肉";
        System.out.println(supplier.get());
    }
}

还要注意的是,基础数据类型还有特定的函数式接口,例如:

这样就不用写泛型了,更加方便。

 


方法引用

正常写法:

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

当执行体里面只有一条函数,且调用的(s)跟左边是一样的,就可以缩写成:

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

对于方法引用,也有各种形式:

1.静态方法引用(类名::方法名):

class Game{
    private String name = "魔兽";

    public static void start(Game game){
        System.out.println(game + "启动");
    }

    @Override
    public String toString() {
        return this.name;
    }
}
public class MethodRefrenceDemo {
    public static void main(String[] args) {
        Consumer<Game> consumer = Game::start;
        Game game = new Game();
        consumer.accept(game);
    }
}

2.非静态方法引用(使用对象实例的方法):

class Game{
    //默认10小时
    private int time = 10;

    public int time(int cost){
        System.out.println("玩了" + cost + "小时");
        this.time -= cost;
        return this.time;
    }
}
public class MethodRefrenceDemo {
    public static void main(String[] args) {
        Game game = new Game();
        Function<Integer , Integer> function = game::time;
        System.out.println("剩余时间:" + function.apply(3) + "小时");
    }
}

结果:

当然,Function出入参都是一样的,根据上面表格,我们可以改写为(当然还可以改成IntUnaryOperator):

UnaryOperator<Integer> function = game::time;

3.使用类名::方法名引用非静态方法:

public class MethodRefrenceDemo {
    public static void main(String[] args) {
        Game game = new Game();
        BiFunction<Game , Integer , Integer> function = Game::time;
        System.out.println("剩余时间:" + function.apply(game ,3) + "小时");
    }
}

因为对于非静态方法,jdk会默认把this对象传过去,所以这里我们需要传对象。

4.构造函数:

public class MethodRefrenceDemo {
    public static void main(String[] args) {
        Supplier<Game> supplier = Game::new;
        System.out.println("新对象:" + supplier.get());
    }
}

结果:

5.有参构造器:

class Game{
    private String name = "魔兽";

    public Game(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Game{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class MethodRefrenceDemo {
    public static void main(String[] args) {
        Function<String , Game> function = Game::new;
        System.out.println("创建了新对象:" + function.apply("炉石"));
    }
}

结果:


类型推断

我们知道lambda是匿名函数,返回实现指定接口的对象,所以我们需要告诉他我们具体实现哪个接口,这就是类型推断。

有如下方式:

@FunctionalInterface
interface ICount{
    int add(int x , int y);
}
public class TypeDemo {
    public static void main(String[] args) {
        //变量类型定义
        ICount lambda = (x , y) -> x + y;
        //数组里
        ICount[] lambdas = {(x , y) -> x + y};
        //强转
        Object lambda2 = (ICount)(x , y) -> x + y;
        //通过返回类型
        ICount lambda3 = createLambda();
    }

    public static ICount createLambda(){
        return (x , y) -> x + y;
    }
}

变量引用

先看代码

public class VarDemo {
    public static void main(String[] args) {
        String str = "我叫";
        Consumer<String> consumer = s -> System.out.println(str + s);
        consumer.accept("耗子肉");
    }
}

其中,如果把str改成别的:

也就是说,虽然str没有加final,但实际上jdk8默认加了final。


级联表达式和柯里化

级联表达式:下面代码可以看到,有多个箭头,这就是级联表达式

public class CurryDemo {
    public static void main(String[] args) {
        Function<Integer , Function<Integer , Integer>> function = 
            x -> y -> x + y;
        System.out.println(function.apply(2).apply(3));
    }
}

柯里化:把多个参数的函数转换为只有一个参数的函数。他可以让函数标准化。

public class CurryDemo {
    public static void main(String[] args) {
        Function<Integer , Function<Integer , Function<Integer , Integer>>> function =
                x -> y -> z -> x + y + z;
        System.out.println(function.apply(2).apply(3).apply(4));

        //也可以写成
        int[] nums = {2 , 3 , 4};
        Function function2 = function;
        for (int i = 0 ; i < nums.length ; i ++){
            if (function2 instanceof Function){
                Object obj = function2.apply(nums[i]);
                if (obj instanceof  Function){
                    function2 = (Function) obj;
                }else {
                    System.out.println("调用结束:" + obj);
                }
            }
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值