Java8新特性:函数式接口,方法与构造器引用

简介: Java8新特性:函数式接口,方法与构造器引用

Java8 新特性

函数式接口(Functional)

通过上面的 Lambda表达式的学习,我们认识了 新的语法,支持这种语法的接口

只包含一个抽象方法的接口,称为函数式接口

你只可以通过 Lambda表达式,来创建该接口的对象,(Lambda表达式抛出一个抛出一个检查异常(即,运行时异常),这个衣长需要在目标接口的抽象方法上进行声明)

我们可以在接口上使用@FunctionalInterface注解,这样做可以检查这个接口是不是函数式接口,同时javadoc也会包含一条声明说明这个接口是一个函数式接口,

java.util.funcion包下定义了Java8丰富的函数式接口

Lambda的表达式本质其实就是函数式接口的实例


什么是函数式接口呢?


Runnable接口就是一个很典型的函数式接口


@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

这里我们自己定一个


@FunctionalInterface

public interface MyinterFace {

   void method1();

}


Java内置四大核心函数式接口

2.png

理论+实践


我们以 Consumer 与 Predicate举例子

/**
 * @author : <h2>冷环渊</h2>
 * @date : 2021/12/11
 * @context: <h4>
 * 消费型接口 Consumer<T> Accept(T t)
 * 供给型接口 Supplier<T> T get()
 * 函数式接口 Function<T> R apply(T t)
 * 断定型接口 Predicate<T> boolean test(T t)
 * </h4>
 */
public class LambdaTest2 {
    /**
     * @author 冷环渊 Doomwatcher
     * @context: Consumer<T> 使用
     * @date: 2021/12/11 14:37
     * @param
     * @return: void
     */
    @Test
    public void Test1() {
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("天上人间的水今天很贵:" + aDouble);
            }
        });
        System.out.println("****************************");
        happyTime(400, money -> System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + money));
    }
    /**
     * @author: 冷环渊 Doomwatcher
     * @context:
     * @date: 2021/12/11 14:33
     * @param money
     * @param con
     * @return void
     */
    public void happyTime(double money, Consumer<Double> con) {
        con.accept(money);
    }
    /**
     * @author 冷环渊 Doomwatcher
     * @context: 断言 Predicate 的使用
     * @date: 2021/12/11 14:39
     * @param
     * @return: void
     */
    @Test
    public void Test2() {
        //传统写法
        List<String> filterList = Arrays.asList("北京", "天津", "南京", "东京", "江南");
        List<String> filterStr = filterString(filterList, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(filterStr);
        System.out.println("*******************************");
        // Lambda表达式 写法
        List<String> filterList1 = Arrays.asList("北京", "天津", "南京", "东京", "江南");
        List<String> filterStr1 = filterString(filterList1, s -> s.contains("京"));
        System.out.println(filterStr1);
    }
    /**
     * @author 冷环渊 Doomwatcher
     * @context:
     * @date: 2021/12/11 14:49
     * @param list
     * @param pre
     * @return: java.util.List<java.lang.String>
     */
    public List<String> filterString(List<String> list, Predicate<String> pre) {
        ArrayList<String> filterlist = new ArrayList<>();
        for (String s : list) {
            if (pre.test(s)) {
                filterlist.add(s);
            }
        }
        return filterlist;
    }
}

方法引用与构造器引用

方法引用


当前要传递给 Lambda体的操作,已经有了实现的方法了,可以使用方法引用

方法引用可以看做是Lambda表达式的深层次表达,换句话说,方法引用就是Lambda表达式,只不过和之前我们自己编写不同,这里通过方法的名字来指向一个方法,可以认为是Lambda表达式的一种语法糖

要求:实现接口的抽象方法的参数列表和返回值类型。必须与方法引用的方法的参数列表和返回值保持一致

格式:使用操作符“::”将类或者对象与方法名字分开来

如下是三种主要的使用情况

对象::实例方法名

类::静态方法名

类::实例方法名

coding

准备两个类 一个是list集合,一个是对象


Employee get/set 和 tostring 为防止无用篇幅过长,这里我们简写只看一下对象属性即可


package Lambda.MethodReferences;
/**
 *
 * @author : <h2>冷环渊</h2>
 * @date : 2021/12/11
 * @context:<h4>提供用于测试的对象</h4>
 */
public class Employee {
    private int id;
    private String name;
    private int age;
    private double salary;
\
}

EmployeeData

package Lambda.MethodReferences;
import java.util.ArrayList;
import java.util.List;
/**
 *
 * @author : <h2>冷环渊</h2>
 * @date : 2021/12/11
 * @context:<h4>提供用于测试的数据</h4>
 */
public class EmployeeData {
    public static List<Employee> getEmployees() {
        List<Employee> list = new ArrayList<>();
        list.add(new Employee(1001, "马化腾", 34, 6000.38));
        list.add(new Employee(1002, "马云", 12, 9876.12));
        list.add(new Employee(1003, "刘强东", 33, 3000.82));
        list.add(new Employee(1004, "雷军", 26, 7657.37));
        list.add(new Employee(1005, "李彦宏", 65, 5555.32));
        list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
        list.add(new Employee(1007, "任正非", 26, 4333.32));
        list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
        return list;
    }
}

方法引用 test

/**
 * @author : <h2>冷环渊</h2>
 * @date : 2021/12/11
 * @context:<h4>
 * 方法引用的使用
 *
 * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
 * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以
 *   方法引用,也是函数式接口的实例。
 * 3. 使用格式:  类(或对象) :: 方法名
 * 4. 具体分为如下的三种情况:
 *    情况1     对象 :: 非静态方法
 *    情况2     类 :: 静态方法
 *    情况3     类 :: 非静态方法
 * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的
 *    形参列表和返回值类型相同!(针对于情况1和情况2)
 *
 * </h4>
 *
 */
public class MethodRefTest {
    // 情况一:对象 :: 实例方法
    //Consumer中的void accept(T t)
    //PrintStream中的void println(T t)
    @Test
    public void test1() {
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("北京");
        System.out.println("*******************");
        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        con2.accept("beijing");
    }
    //Supplier中的T get()
    //Employee中的String getName()
    @Test
    public void test2() {
        Employee emp = new Employee(1001, "Tom", 23, 5600);
        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());
        System.out.println("*******************");
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }
    // 情况二:类 :: 静态方法
    //Comparator中的int compare(T t1,T t2)
    //Integer中的int compare(T t1,T t2)
    @Test
    public void test3() {
        Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
        System.out.println(com1.compare(12, 21));
        System.out.println("*******************");
        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(12, 3));
    }
    //Function中的R apply(T t)
    //Math中的Long round(Double d)
    @Test
    public void test4() {
        Function<Double, Long> func = new Function<Double, Long>() {
            @Override
            public Long apply(Double d) {
                return Math.round(d);
            }
        };
        System.out.println("*******************");
        Function<Double, Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(12.3));
        System.out.println("*******************");
        Function<Double, Long> func2 = Math::round;
        System.out.println(func2.apply(12.6));
    }
    // 情况三:类 :: 实例方法  (有难度)
    // Comparator中的int comapre(T t1,T t2)
    // String中的int t1.compareTo(t2)
    @Test
    public void test5() {
        Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc", "abd"));
        System.out.println("*******************");
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abd", "abm"));
    }
    //BiPredicate中的boolean test(T t1, T t2);
    //String中的boolean t1.equals(t2)
    @Test
    public void test6() {
        BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
        System.out.println(pre1.test("abc", "abc"));
        System.out.println("*******************");
        BiPredicate<String, String> pre2 = String::equals;
        System.out.println(pre2.test("abc", "abd"));
    }
    // Function中的R apply(T t)
    // Employee中的String getName();
    @Test
    public void test7() {
        Employee employee = new Employee(1001, "Jerry", 23, 6000);
        Function<Employee, String> func1 = e -> e.getName();
        System.out.println(func1.apply(employee));
        System.out.println("*******************");
        Function<Employee, String> func2 = Employee::getName;
        System.out.println(func2.apply(employee));
    }
}

构造方法引用


通过 简化 的方式,来调用不同的构造器


一、构造器引用


和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。

抽象方法的返回值类型即为构造器所属的类的类型


二、数组引用


大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。

/**
 *
 * @author : <h2>冷环渊</h2>
 * @date : 2021/12/11
 * @context:<h4>
 *
 * 一、构造器引用
 *      和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
 *      抽象方法的返回值类型即为构造器所属的类的类型
 *
 * 二、数组引用
 *     大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。
 * </h4>
 */
public class ConstructorRefTest {
    //构造器引用
    //Supplier中的T get()
    //Employee的空参构造器:Employee()
    @Test
    public void test1() {
        Supplier<Employee> sup = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        System.out.println("*******************");
        Supplier<Employee> sup1 = () -> new Employee();
        System.out.println(sup1.get());
        System.out.println("*******************");
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());
    }
    //Function中的R apply(T t)
    @Test
    public void test2() {
        Function<Integer, Employee> func1 = id -> new Employee(id);
        Employee employee = func1.apply(1001);
        System.out.println(employee);
        System.out.println("*******************");
        Function<Integer, Employee> func2 = Employee::new;
        Employee employee1 = func2.apply(1002);
        System.out.println(employee1);
    }
    //BiFunction中的R apply(T t,U u)
    @Test
    public void test3() {
        BiFunction<Integer, String, Employee> func1 = (id, name) -> new Employee(id, name);
        System.out.println(func1.apply(1001, "Tom"));
        System.out.println("*******************");
        BiFunction<Integer, String, Employee> func2 = Employee::new;
        System.out.println(func2.apply(1002, "Tom"));
    }
    //数组引用
    //Function中的R apply(T t)
    @Test
    public void test4() {
        Function<Integer, String[]> func1 = length -> new String[length];
        String[] arr1 = func1.apply(5);
        System.out.println(Arrays.toString(arr1));
        System.out.println("*******************");
        Function<Integer, String[]> func2 = String[]::new;
        String[] arr2 = func2.apply(10);
        System.out.println(Arrays.toString(arr2));
    }
}

总结

全新的语法带来了很多的便捷,理解起来可能相对麻烦

这里在改变语法为Lambda的时候,可以自己找找可以省去,那一些部分

思考,为什么构造器引用可以根据数量和类型去找到对应的构造器


相关文章
|
7天前
|
数据采集 JSON Java
利用Java获取京东SKU接口指南
本文介绍如何使用Java通过京东API获取商品SKU信息。首先,需注册京东开放平台账号并创建应用以获取AppKey和AppSecret。接着,查阅API文档了解调用方法。明确商品ID后,构建请求参数并通过HTTP客户端发送请求。最后,解析返回的JSON数据提取SKU信息。注意遵守API调用频率限制及数据保护法规。此方法适用于电商平台及其他数据获取场景。
|
12天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
43 6
|
17天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
26天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
42 6
|
1月前
|
安全 Java 开发者
Java中WAIT和NOTIFY方法必须在同步块中调用的原因
在Java多线程编程中,`wait()`和`notify()`方法是实现线程间协作的关键。这两个方法必须在同步块或同步方法中调用,这一要求背后有着深刻的原因。本文将深入探讨为什么`wait()`和`notify()`方法必须在同步块中调用,以及这一机制如何确保线程安全和避免死锁。
41 4
|
28天前
|
Java 数据处理 数据安全/隐私保护
Java处理数据接口方法
Java处理数据接口方法
26 1
|
29天前
|
Java API
Java中内置的函数式接口
Java中内置的函数式接口
25 2
|
Java
JAVA方法的定义
JAVA方法的定义
97 0
|
6月前
|
安全 Java 编译器
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
46 1
|
7月前
|
存储 Java
Java数组与带参数方法:定义、调用及实践
Java数组与带参数方法:定义、调用及实践
80 1