【jdk8新特性】lambda表达式函数式接口

简介: 【jdk8新特性】lambda表达式函数式接口

【jdk8新特性】lambda表达式函数式接口

什么是函数式接口

函数式接口在Java中是指:有且仅有一个抽象方法的接口。

为什么需要函数式接口

因为写lambda表达式的前提是需要参数或者变量为函数式接口

自定义函数接口

例子:

public class Demo01UserFunctionalInterface {
    public static void main(String[] args) {
        test((int a, int b) -> {
            System.out.println(a + b);
        });
    }
    public static void test(Operation op) {
        op.getSum(1, 2);
    }
}
interface Operation {
    public abstract void getSum(int a, int b);
}

结果

3

这个例子中Operation就是函数式接口

常用的内置函数接口

Supplier

源代码

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

可以看出这个函数式接口的抽象方法是get,且不需要参数,所以这个Supplier接口的作用是 无需传参处理数据获得返回值,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。

例子

public class Demo02Supplier {
    // 使用Lambda表达式返回数组元素最大值
    public static void main(String[] args) {
        System.out.println("开始了");
        printMax(() -> {
            int[] arr = {11, 99, 88, 77, 22};
            Arrays.sort(arr); // 升序排序
            return arr[arr.length - 1];
        });
    }
    public static void printMax(Supplier<Integer> supplier) {
        System.out.println("aa");
        int max = supplier.get();
        System.out.println("max = " + max);
    }
}

结果

开始了
aa
max = 99

可以看出 printMax方法的参数类型是 Supplier函数式接口 所以我们可以使用lambda表达式,我们重写了 Supplier的get方法 内容是一个链表的升序排序 并返回最大值

Consumer

源代码

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

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

可以看出 Consumer函数式接口 只有一个抽象方法accept(T t) 以及一个 默认方法andThen(Consumer),accept方法需要一个参数并且没有返回值,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。

例子

public class Demo03Consumer {
    // 使用Lambda表达式将一个字符串转成大写的字符串
    public static void main(String[] args) {
        System.out.println("开始啦");
        printHello((String str) -> {
            System.out.println(str.toUpperCase());
        });
    }

    public static void printHello(Consumer<String> consumer) {
        System.out.println("aaa");
        consumer.accept("Hello World");
    }
}

可以看出 printHello方法的参数类型是 Consumer函数式接口 所以我们可以使用lambda表达式,我们重写了 Supplier的accept方法 内容是一个链表的升序排序 并返回最大值

默认方法:andThen

如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。

例子

public class Demo04ConsumerAndThen {
    // 使用Lambda表达式先将一个字符串转成小写的字符串,再转成大写
    public static void main(String[] args) {
        System.out.println("开始啦");
        printHello((String str) -> {
            System.out.println(str.toLowerCase());
        }, (String str) -> {
            System.out.println(str.toUpperCase());
        });
    }

    public static void printHello(Consumer<String> c1, Consumer<String> c2) {
        System.out.println("aa");
        String str = "Hello World";
        // c1.accept(str);
        // c2.accept(str);

        c2.andThen(c1).accept(str);

    }
}

结果

aa
HELLO WORLD
hello world

可以看出运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

Function

源代码

public interface Function<T, R> {

    R apply(T t);
    
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

可以看出 Function函数式接口 只有一个抽象方法R apply(T t),需要参数和返回值,用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。

例子

public class Demo05Function {
    // 使用Lambda表达式将字符串转成数字
    public static void main(String[] args) {
        System.out.println("开始");
        getNumber((String str) -> {
            int i = Integer.parseInt(str);
            return i;
        });
    }

    public static void getNumber(Function<String, Integer> function) {
        System.out.println("aa");
        Integer num1 = function.apply("10");
        System.out.println("num1 = " + num1);
    }
}

结果

开始
aa
num1 = 10

可以看出 Function接口的apply方法 对字符串10 进行处理 返回整型10

Predicate接口

public interface Predicate<T> {

    boolean test(T t);


    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }


    default Predicate<T> negate() {
        return (t) -> !test(t);
    }


    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }


    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }


    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

可以看出 Predicate函数式接口 只有一个抽象方法 boolean test(T t);需要一个参数并返回boolean类型,有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以使用Predicate接口。

例子

public class Demo07Predicate {
    // 使用Lambda判断一个人名如果超过3个字就认为是很长的名字
    public static void main(String[] args) {
        System.out.println("开始啦");
        isLongName((String name) -> {
            return name.length() > 3;
        });
    }

    public static void isLongName(Predicate<String> predicate) {
        System.out.println("aa");
        boolean isLong = predicate.test("迪丽热巴");
        System.out.println("是否是长名字: " + isLong);
    }
}

结果

开始啦
aa
是否是长名字: true

可以看出传入一个长度为4的字符串 test方法里进行判断是否 长度>3 返回true

默认方法:and,or,negate

解释

and:将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。

or:将两个 Predicate 条件使用“与”逻辑连接起来实 现“或”的效果时,可以使用default方法 or。

negate:Predicate 中的"取反,非"。

例子

// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中不包含W
public static void main(String[] args) {
    test((String str) -> {
        // 判断是否包含W
        return str.contains("W");
    }, (String str) -> {
        // 判断是否包含H
        return str.contains("H");
    });
}

public static void test(Predicate<String> p1, Predicate<String> p2) {
    // String str = "Hello orld";
    // boolean b1 = p1.test(str);
    // boolean b2 = p2.test(str);
    // if (b1 && b2) {
    //     System.out.println("即包含W,也包含H");
    // }
    // 使用Lambda表达式判断一个字符串中即包含W,也包含H
    String str = "Hello World";
    boolean b = p1.and(p2).test(str);
    if (b) {
        System.out.println("即包含W,也包含H");
    }

    // 使用Lambda表达式判断一个字符串中包含W或者包含H
    boolean b1 = p1.or(p2).test(str);
    if (b1) {
        System.out.println("包含W或者包含H");
    }
    // 使用Lambda表达式判断一个字符串中不包含W
    boolean b2 = p1.negate().test("Hello W");
    // negate相当于取反 !boolean
    if (b2) {
        System.out.println("不包含W");
    }
}

结果

即包含W,也包含H
包含W或者包含H
目录
相关文章
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
27 1
|
2月前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
46 3
|
1月前
|
存储 安全 Java
JDK1.8 新的特性
JDK1.8 新的特性
19 0
|
2月前
|
编解码 安全 Java
jdk8新特性-接口和日期处理
jdk8新特性-接口和日期处理
|
2月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
335 3
|
3月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
58 1
|
3月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
701 4
|
4月前
|
Java Linux
Linux复制安装 jdk 环境
Linux复制安装 jdk 环境
109 3
|
1月前
|
Oracle Java 关系型数据库
jdk17安装全方位手把手安装教程 / 已有jdk8了,安装JDK17后如何配置环境变量 / 多个不同版本的JDK,如何配置环境变量?
本文提供了详细的JDK 17安装教程,包括下载、安装、配置环境变量的步骤,并解释了在已有其他版本JDK的情况下如何管理多个JDK环境。
781 0
|
3月前
|
Java 开发工具
开发工具系列 之 同一个电脑上安装多个版本的JDK
这篇文章介绍了如何在一台电脑上安装和配置多个版本的JDK,包括从官网下载所需JDK、安装过程、配置环境变量以及如何查看和切换当前使用的JDK版本,并提到了如果IDEA和JDK版本不兼容时的解决方法。
开发工具系列 之 同一个电脑上安装多个版本的JDK