Java粗浅认识-Java 8 lambda 表达式

本文深入探讨Java 1.8的Lambda表达式,包括基本使用、函数式接口、方法引用及预定义函数式接口,如Predicate、Function、Consumer和Supplier等。同时,介绍了数值类型函数式接口及其命名规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、lambda表达式实例

提到lambda表达式,我们一般用的最多的就是foreach遍历List、Set、Queue、Map等。

/**
     * 容器forEache遍历
     */
    public static void forEach() {
        //list
        List<String> list = new ArrayList<>();
        list.forEach(s -> System.out.println(s));

        //set
        Set<String> set = new HashSet<>();
        set.forEach(s -> System.out.println(s));

        //queue
        Queue<String> queue = new PriorityQueue();
        queue.forEach(s -> System.out.println(s));

        //deque
        Deque<String> deque = new LinkedList();
        deque.forEach(s -> System.out.println(s));

        //Map
        Map<String, Object> map = new HashMap();
        map.forEach((s, o) -> System.out.println(s + ":" + o));
    }

当然,我们可以自己实现写一个函数式接口,然后通过lambda表达式来编写我们自己的代码。

    public static void main(String[] args) {
        fooFun("测试打印效果",target -> System.out.println(target));
    }

    /**
     * 随意一个方法
     * @param target 需要被答应的内容
     * @param printer 打印机
     */
    public static void fooFun(String target,FunctionInterface printer){
        printer.print(target);
    }

    /**
     * 函数式接口,@FunctionalInterface可加可不加,FunctionalInterface属于target=Runtime
     * ,编译时已经被jvm处理过
     */
    @FunctionalInterface
    public interface FunctionInterface<T>{
        /**
         * 接口函数
         * @param target 需要被打印的目标
         */
        void print(T target);
    }

 分析:

1.定义一个函数式接口(对应的,public interface FunctionInterface<T>);

2.在自己定义的方法中,传入这个已经定义好的函数式接口(fooFun(String target,FunctionInterface printer););

3在使用方法时,就能用lambda表达表达式了。(fooFun("测试打印效果",target -> System.out.println(target));)

二、什么是lambda表达式

这里演示了lambda表达式的基本使用,接下来我们来看看什么是lambda表达式。

lambda表达式我们可以理解为传递匿名函数的一种方式,这种表达式没有方法名,有参数列表,有方法体,有返回值,可能有异常抛出(通常不推荐抛出异常,java默认实现也没有抛出异常,只有自己实现时,才会根据需要抛出),能够当作参数带入方法中。

lambda表达式
lambda表达式

 总结起来分两种:

(parameters) -> expression

(parameters) -> {statements;}

三、函数式接口、接口函数、静态方法、默认方法、方法引用

上文提到了函数式接口,接口方法,接下来我们了解一下什么是函数式接口,什么是接口方法。

函数式接口就是只定义了一个抽象方法的接口。

/**
 * @author baopz
 */
@FunctionalInterface
public interface FunctionInterface {
    @Override
    int hashCode();

    @Override
    boolean equals(Object obj);

    @Override
    String toString();

    /**
     * 唯一的抽象方法
     */
    void test();

    /**
     * 默认方法
     */
    default void foo() {}

    /**
     * 静态方法
     */
    static void bar() {}
}

 分析:

1.代码中,接口Override了来自Object的三个方法hashCode()、equals()、toString()

2.唯一的抽象方法test(),用来在lambda表达式中唯一实现的方法,也就是接口函数

3.有个带有方法体的,由default修饰的默认方法foo(),可以理解为实现了该接口的子类,会顺带继承的方法,默认方法

4.有个带有方法体的,由static修饰的静态方法bar(),可以理解为工具方法,静态方法

方法引用,仅仅涉及到单一方法的使用时,提供的一种更加简洁的写法的语法糖。

四、方法引用

指向静态方法的方法引用,Integer::parseInt
指向任意类型实例方法的引用,String::length
指向现有对象的实例方法的方法引用,methodReferences::length
构造函数数组构造函数父类调用(super-call)的一些特殊形式的方法引用

实例:

/**
 * 方法引用事例
 * @author baopz
 */
public class MethodReferences {
    public static void main(String[] args) {
        //静态方法的方法引用
        staticFun("123", Integer::parseInt);

        //任意类型实例方法的引用
        instanceFun("foo", String::length);

        //构造函数
        MethodReferences methodReferences = createInstance(MethodReferences::new);

        //数组构造函数
        MethodReferences[] methodRes = createArrayInstance(3, MethodReferences[]::new);

        //现有对象的实例方法的方法引用
        curInstanceFun("foo", methodReferences::length);
    }

    public static void staticFun(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }

    public static void instanceFun(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }

    public static MethodReferences createInstance(Supplier<MethodReferences> supplier) {
        return supplier.get();
    }

    public static MethodReferences[] createArrayInstance(int length, Function<Integer, MethodReferences[]> function) {
       return function.apply(length);
    }

    public static int curInstanceFun(String str, Function<String, Integer> function) {
        return function.apply(str);
    }

    public int length(String str) {
        return str.length();
    }
}

五、java默认提供的几类函数式接口

上文中使用了Function,Supplier,Consumer等接口,这些接口都来自,java1.8 新增的java.util.function包中,是java自带的一些默认函数式接口

```

Predicate<T>           T->boolean
Function<T,R>         T->R
Consumer<T>         T-void
Supplier<T>            void -> T
...

数值类型函数式接口

```

* Predicate<T>,谓词,语义是传入任意一个类型T,经过处理(Predicate的接口方法test()),返回一个boolean类型的值。

    public static boolean isDog(Dog dog, Predicate<Dog> predicate){
        return predicate.test(dog);
    }
   
    public static void main(String[] args) {
        Dog.isDog(new Dog(),dog -> dog instanceof Dog);
    }

*  Function<T,R> ,方法,语义是传入一个任意类型T,经过处理(Function 的接口方法apply()),返回一个R类型的值

    public static void main(String[] args) {
        //基础lambda表达式
        Bar.length("foo",s -> s.length());
        //方法引用的写法
        Bar.length("foo",String::length);
    }

    public static int length(String foo, Function<String,Integer> function){
        return function.apply(foo);
    }

* Consumer<T>,消费者,语义是传入一个任意类型的T,用方法处理(Consumer的接口方法accept()),返回一个void(就是没有返回值)。

    public static void main(String[] args) {
        Dog.run(new Dog(),dog -> System.out.println("dog running."));
    }

    public static void run(Dog dog, Consumer<Dog> consumer){
        consumer.accept(dog);
    }

* Supplier<T>,提供者,语义是不用传递任何参数,直接通过方法(Supplier的接口方法get())产生一个T类型,并返回。

    public static void main(String[] args) {
        //lambda基本表达式
        Dog dog = Dog.get(() -> new Dog());
        //方法引用写法
        dog = Dog.get(Dog::new);
    }

    public static Dog get(Supplier<Dog> supplier){
        return supplier.get();
    }

数值类型函数式接口,我们都知道在java中对象类型分两类,基础类型和引用类型,得益于java1.5提供的类型自动装箱,自动拆箱,用Integer、Double、Long等计算是拆箱为int、double、long等来进行数值计算,但自动装拆箱有效率损耗,所以lambda为了消除这种效率损耗,提供了数值类型函数式接口,如IntFunction、DoublePredicate、LongConsumer等(还有很多)

    public static void main(String[] args) {
        add(10, 20, (left, right) -> left + right);
    }

    public static int add(int a, int b, IntBinaryOperator operator) {
        return operator.applyAsInt(a, b);
    }

这里类型比较多,就不一一介绍,建议各位小伙伴如果要深入学习,建议把所有的类型都练习一遍。

由于类型比较多,类名不容易记住,java对这些类的命名有个小诀窍,

就是针对专门的输入参数类型的函数式接口,

1.名称都要加上对应的原始类型前缀(DoubleConsumer),

2.如果输入参数有两个的,都有Binary关键字(LongBinaryOperator),

3.如果没有具体类型的两个参数类型传入,则是以Bi为前缀(BiConsumer)

4.如果只知道返回类型的,都是以To为前缀(ToIntFunction)

六、思考

java1.8的lambda内容讲完了这里给大家留个小问题,如果传入的参数是三个或多个,该如何处理呢? 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值