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的时候,可以自己找找可以省去,那一些部分

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


相关文章
|
9天前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
115 1
|
9天前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
110 4
|
14天前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
163 5
|
18天前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
69 11
|
20天前
|
Java API 数据处理
Java新特性:使用Stream API重构你的数据处理
Java新特性:使用Stream API重构你的数据处理
|
22天前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
Java API 开发者
47 0
|
2月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
177 23
|
7月前
|
存储 JSON Java
《从头开始学java,一天一个知识点》之:方法定义与参数传递机制
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 🚀 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。上篇:《输入与输出:Scanner与System类》 | 下篇剧透:《方法重载与可变参数》。
147 25
|
安全 Java 编译器
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
杭州 【Java基础知识 11】java泛型方法的定义和使用(学习+改进+自己理解,想法) (借鉴-侵-删)
128 1

热门文章

最新文章

下一篇
oss教程