第18.3章 Java8新特性:函数式(Functional)接口

3. Java8新特性:函数式(Functional)接口

3.1 什么是函数式接口

  • 只包含一个抽象方法(Single Abstract Method,简称SAM)的接口,称为函数式接口。当然该接口可以包含其他非抽象方法。

  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。

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

  • 在java.util.function包下定义了Java 8 的丰富的函数式接口

3.2 如何理解函数式接口

  • Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,即Java不但可以支持OOP还可以支持OOF(面向函数编程)

  • Java8引入了Lambda表达式之后,Java也开始支持函数式编程。

  • Lambda表达式不是Java最早使用的。目前C++,C#,Python,Scala等均支持Lambda表达式。

  • 面向对象的思想:

  • 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。

  • 函数式编程思想:

  • 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。

  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。

  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。

3.3 举例

举例1:

举例2:

作为参数传递 Lambda 表达式:

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

3.4 Java 内置函数式接口

3.4.1 之前的函数式接口

之前学过的接口,有些就是函数式接口,比如:

  • java.lang.Runnable

  • public void run()

  • java.lang.Iterable

  • public Iterator iterate()

  • java.lang.Comparable

  • public int compareTo(T t)

  • java.util.Comparator

  • public int compare(T t1, T t2)

3.4.2 四大核心函数式接口

函数式接口

称谓

参数类型

用途

Consumer<T>

消费型接口

T

对类型为T的对象应用操作,包含方法: void accept(T t)

Supplier<T>

供给型接口

返回类型为T的对象,包含方法:T get()

Function<T, R>

函数型接口

T

对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)

Predicate<T>

判断型接口

T

确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t)

package com.test;
 
import org.junit.Test;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
 
/**
 * CLassName: LambdaTest2
 * Package: com.test
 * Description:
 *
 * Java内置的4大核心函数式接口
 *
 * 消费型接口 Consumer<T>    void accept(T t)
 * 供给型接口 Supplier<T>    T get()
 * 函数式接口 Function<T,R>  R apply(T t)
 * 断定型接口 Predicate<T>   boolean test(T t)
 *
 * @Author: JOKER-wqc
 * @Version: v1.0
 */
public class LambdaTest2 {
 
    @Test
    public void test(){
        consumer(25, new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println("消费了"+integer+"元");
            }
        });
 
        System.out.println("*****************************");
        consumer(26,money ->System.out.println("消费了"+money+"元"));
 
    }
 
    public void consumer(int money, Consumer<Integer> func){
        func.accept(money);
 
    }
 
 
    @Test
    public void test1(){
        List<String> lst = Arrays.asList("东海", "南海", "北海", "中南海", "西海", "爱情海", "大洋洲");
        List<String> reslst = filterList(lst, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("海");
            }
        });
        System.out.println(reslst);
        System.out.println("************************");
 
        List<String> reslst1 = filterList(lst,s -> s.contains("南"));
        System.out.println(reslst1);
 
    }
    //根据给定规则,过滤集合中的字符串,此规则由Predicate的方法决定
    public List<String> filterList(List<String> lst, Predicate<String> filter){
        List<String> result = new ArrayList<String>();
        for(String s:lst){
            if(filter.test(s)){
                result.add(s);
            }
        }
        return result;
    }
 
 
}
3.4.3 其它接口

类型1:消费型接口

消费型接口的抽象方法特点:有形参,但是返回值类型是void

接口名

抽象方法

描述

BiConsumer<T,U>

void accept(T t, U u)

接收两个对象用于完成功能

DoubleConsumer

void accept(double value)

接收一个double值

IntConsumer

void accept(int value)

接收一个int值

LongConsumer

void accept(long value)

接收一个long值

ObjDoubleConsumer

void accept(T t, double value)

接收一个对象和一个double值

ObjIntConsumer

void accept(T t, int value)

接收一个对象和一个int值

ObjLongConsumer

void accept(T t, long value)

接收一个对象和一个long值

类型2:供给型接口

这类接口的抽象方法特点:无参,但是有返回值

接口名

抽象方法

描述

BooleanSupplier

boolean getAsBoolean()

返回一个boolean值

DoubleSupplier

double getAsDouble()

返回一个double值

IntSupplier

int getAsInt()

返回一个int值

LongSupplier

long getAsLong()

返回一个long值

类型3:函数型接口

这类接口的抽象方法特点:既有参数又有返回值

接口名

抽象方法

描述

UnaryOperator

T apply(T t)

接收一个T类型对象,返回一个T类型对象结果

DoubleFunction

R apply(double value)

接收一个double值,返回一个R类型对象

IntFunction

R apply(int value)

接收一个int值,返回一个R类型对象

LongFunction

R apply(long value)

接收一个long值,返回一个R类型对象

ToDoubleFunction

double applyAsDouble(T value)

接收一个T类型对象,返回一个double

ToIntFunction

int applyAsInt(T value)

接收一个T类型对象,返回一个int

ToLongFunction

long applyAsLong(T value)

接收一个T类型对象,返回一个long

DoubleToIntFunction

int applyAsInt(double value)

接收一个double值,返回一个int结果

DoubleToLongFunction

long applyAsLong(double value)

接收一个double值,返回一个long结果

IntToDoubleFunction

double applyAsDouble(int value)

接收一个int值,返回一个double结果

IntToLongFunction

long applyAsLong(int value)

接收一个int值,返回一个long结果

LongToDoubleFunction

double applyAsDouble(long value)

接收一个long值,返回一个double结果

LongToIntFunction

int applyAsInt(long value)

接收一个long值,返回一个int结果

DoubleUnaryOperator

double applyAsDouble(double operand)

接收一个double值,返回一个double

IntUnaryOperator

int applyAsInt(int operand)

接收一个int值,返回一个int结果

LongUnaryOperator

long applyAsLong(long operand)

接收一个long值,返回一个long结果

BiFunction<T,U,R>

R apply(T t, U u)

接收一个T类型和一个U类型对象,返回一个R类型对象结果

BinaryOperator

T apply(T t, T u)

接收两个T类型对象,返回一个T类型对象结果

ToDoubleBiFunction<T,U>

double applyAsDouble(T t, U u)

接收一个T类型和一个U类型对象,返回一个double

ToIntBiFunction<T,U>

int applyAsInt(T t, U u)

接收一个T类型和一个U类型对象,返回一个int

ToLongBiFunction<T,U>

long applyAsLong(T t, U u)

接收一个T类型和一个U类型对象,返回一个long

DoubleBinaryOperator

double applyAsDouble(double left, double right)

接收两个double值,返回一个double结果

IntBinaryOperator

int applyAsInt(int left, int right)

接收两个int值,返回一个int结果

LongBinaryOperator

long applyAsLong(long left, long right)

接收两个long值,返回一个long结果

类型4:判断型接口

这类接口的抽象方法特点:有参,但是返回值类型是boolean结果。

接口名

抽象方法

描述

BiPredicate<T,U>

boolean test(T t, U u)

接收两个对象

DoublePredicate

boolean test(double value)

接收一个double值

IntPredicate

boolean test(int value)

接收一个int值

LongPredicate

boolean test(long value)

接收一个long值

3.4.4 内置接口代码演示

举例1:

package com.atguigu.four;

import java.util.Arrays;
import java.util.List;

public class TestConsumer {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java","c","python","c++","VB","C#");
        //遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。
        list.forEach(s -> System.out.println(s));
    }
}

举例2:

package com.atguigu.four;

import java.util.function.Supplier;

public class TestSupplier {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "尚硅谷";
        System.out.println(supplier.get());
    }
}

举例3:

package com.atguigu.four;

import java.util.ArrayList;

public class TestPredicate {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("atguigu");
        list.add("ok");
        list.add("yes");

        System.out.println("删除之前:");
        list.forEach(t-> System.out.println(t));
		
        //用于删除集合中满足filter指定的条件判断的。
        //删除包含o字母的元素
        list.removeIf(s -> s.contains("o"));

        System.out.println("删除包含o字母的元素之后:");
        list.forEach(t-> System.out.println(t));
    }
}

举例4:

package com.atguigu.four;

import java.util.function.Function;

public class TestFunction {
    public static void main(String[] args) {
        //使用Lambda表达式实现Function<T,R>接口,可以实现将一个字符串首字母转为大写的功能。
        Function<String,String> fun = s -> s.substring(0,1).toUpperCase() + s.substring(1);
        System.out.println(fun.apply("hello"));
    }
}
3.4.5 练习

练习1:无参无返回值形式

假如有自定义函数式接口Call如下:

public interface Call {
    void shout();
}

在测试类中声明一个如下方法:

public static void callSomething(Call call){
		call.shout();
}

在测试类的main方法中调用callSomething方法,并用Lambda表达式为形参call赋值,可以喊出任意你想说的话。

public class TestLambda {
	public static void main(String[] args) {
		callSomething(()->System.out.println("回家吃饭"));
		callSomething(()->System.out.println("我爱你"));
		callSomething(()->System.out.println("滚蛋"));
		callSomething(()->System.out.println("回来"));
	}
	public static void callSomething(Call call){
		call.shout();
	}
}
interface Call {
    void shout();
}

练习2:消费型接口

代码示例:Consumer 接口

在JDK1.8中Collection集合接口的父接口Iterable接口中增加了一个默认方法:

public default void forEach(Consumer<? super T> action) 遍历Collection集合的每个元素,执行“xxx消费型”操作。

在JDK1.8中Map集合接口中增加了一个默认方法:

public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。

案例:

(1)创建一个Collection系列的集合,添加一些字符串,调用forEach方法遍历查看

(2)创建一个Map系列的集合,添加一些(key,value)键值对,调用forEach方法遍历查看

示例代码:

	@Test
	public void test1(){
		List<String> list = Arrays.asList("hello","java","lambda","atguigu");
		list.forEach(s -> System.out.println(s));
    }
	@Test
	public void test2(){
		HashMap<Integer,String> map = new HashMap<>();
		map.put(1, "hello");
		map.put(2, "java");
		map.put(3, "lambda");
		map.put(4, "atguigu");
		map.forEach((k,v) -> System.out.println(k+"->"+v));
	}

练习3:供给型接口

代码示例:Supplier 接口

在JDK1.8中增加了StreamAPI,java.util.stream.Stream 是一个数据流。这个类型有一个静态方法:

public static <T> Stream<T> generate(Supplier<T> s)可以创建Stream的对象。而又包含一个forEach方法可以遍历流中的元素:public void forEach(Consumer<? super T> action)。

案例:

现在请调用Stream的generate方法,来产生一个流对象,并调用Math.random()方法来产生数据,为Supplier函数式接口的形参赋值。最后调用forEach方法遍历流中的数据查看结果。

	@Test
	public void test2(){
		Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num));
	}

练习4:功能型接口

代码示例:Function<T,R>接口

在JDK1.8时Map接口增加了很多方法,例如:

public default void replaceAll(BiFunction<? super K,? super V,? extends V> function) 按照function指定的操作替换map中的value。

public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。

案例:

(1)声明一个Employee员工类型,包含编号、姓名、薪资。

(2)添加n个员工对象到一个HashMap<Integer,Employee>集合中,其中员工编号为key,员工对象为value。

(3)调用Map的forEach遍历集合

(4)调用Map的replaceAll方法,将其中薪资低于10000元的,薪资设置为10000。

(5)再次调用Map的forEach遍历集合查看结果

Employee类:

class Employee{
	private int id;
	private String name;
	private double salary;
	public Employee(int id, String name, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	public Employee() {
		super();
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + "]";
	}
	
}

测试类:

import java.util.HashMap;

public class TestLambda {
	public static void main(String[] args) {
		HashMap<Integer,Employee> map = new HashMap<>();
		Employee e1 = new Employee(1, "张三", 8000);
		Employee e2 = new Employee(2, "李四", 9000);
		Employee e3 = new Employee(3, "王五", 10000);
		Employee e4 = new Employee(4, "赵六", 11000);
		Employee e5 = new Employee(5, "钱七", 12000);
		
		map.put(e1.getId(), e1);
		map.put(e2.getId(), e2);
		map.put(e3.getId(), e3);
		map.put(e4.getId(), e4);
		map.put(e5.getId(), e5);
		
		map.forEach((k,v) -> System.out.println(k+"="+v));
		System.out.println();
		
		map.replaceAll((k,v)->{
			if(v.getSalary()<10000){
				v.setSalary(10000);
			}
			return v;
		});
		map.forEach((k,v) -> System.out.println(k+"="+v));
	}
}

练习5:判断型接口

代码示例:Predicate 接口

JDK1.8时,Collecton 接口增加了一下方法,其中一个如下:

public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。

public default void forEach(Consumer<? super T> action) 遍历Collection集合的每个元素,执行“xxx消费型”操作。

案例:

(1)添加一些字符串到一个Collection集合中

(2)调用forEach遍历集合

(3)调用removeIf方法,删除其中字符串的长度<5的

(4)再次调用forEach遍历集合

import java.util.ArrayList;

public class TestLambda {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		list.add("hello");
		list.add("java");
		list.add("atguigu");
		list.add("ok");
		list.add("yes");
		
		list.forEach(str->System.out.println(str));
		System.out.println();
		
		list.removeIf(str->str.length()<5);
		list.forEach(str->System.out.println(str));
	}
}

练习6:判断型接口

案例:

(1)声明一个Employee员工类型,包含编号、姓名、性别,年龄,薪资。

(2)声明一个EmployeeSerice员工管理类,包含一个ArrayList 集合的属性all,在EmployeeSerice的构造器中,创建一些员工对象,为all集合初始化。

(3)在EmployeeSerice员工管理类中,声明一个方法:ArrayList get(Predicate p),即将满足p指定的条件的员工,添加到一个新的ArrayList 集合中返回。

(4)在测试类中创建EmployeeSerice员工管理类的对象,并调用get方法,分别获取:

  • 所有员工对象

  • 所有年龄超过35的员工

  • 所有薪资高于15000的女员工

  • 所有编号是偶数的员工

  • 名字是“张三”的员工

  • 年龄超过25,薪资低于10000的男员工

示例代码:

Employee类:

public class Employee{
	private int id;
	private String name;
	private char gender;
	private int age;
	private double salary;
	
	public Employee(int id, String name, char gender, int age, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.gender = gender;
		this.age = age;
		this.salary = salary;
	}
	public Employee() {
		super();
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
				+ "]";
	}
}

员工管理类:

class EmployeeService{
	private ArrayList<Employee> all;
	public EmployeeService(){
		all = new ArrayList<Employee>();
		all.add(new Employee(1, "张三", '男', 33, 8000));
		all.add(new Employee(2, "翠花", '女', 23, 18000));
		all.add(new Employee(3, "无能", '男', 46, 8000));
		all.add(new Employee(4, "李四", '女', 23, 9000));
		all.add(new Employee(5, "老王", '男', 23, 15000));
		all.add(new Employee(6, "大嘴", '男', 23, 11000));
	}
	public ArrayList<Employee> get(Predicate<Employee> p){
		ArrayList<Employee> result = new ArrayList<Employee>();
		for (Employee emp : result) {
			if(p.test(emp)){
				result.add(emp);
			}
		}
		return result;
	}
}

测试类:

public class TestLambda {
	public static void main(String[] args) {
		EmployeeService es = new EmployeeService();
		
		es.get(e -> true).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getAge()>35).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getSalary()>15000 && e.getGender()=='女').forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getId()%2==0).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> "张三".equals(e.getName())).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getAge()>25 && e.getSalary()<10000 && e.getGender()=='男').forEach(e->System.out.println(e));
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值