函数式接口与lambda表达式--初级Java开发学习笔记

157 阅读4分钟

函数式接口与lambda表达式--初级Java开发学习笔记

函数式编程的一种解释

之前看到一篇文章这样来解释“函数式编程”:

函数:函数式接口

式:lamda表达式

这种说法明显比较牵强,有点强行解释的感觉,但进一步学习后感觉从函数式接口和lamda表达式这两个方面切入stream的学习也是不错的。

函数式接口

先不谈函数式接口的定义,直接来看两个函数式接口

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类型,没有返回值;

BiFunction接口

其定义如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
    
    //暂时忽略这个方法
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

BiFunction接口的apply方法被定义为可以传入两个不同类型的参数,返回另一个类型的返回值;

比较两个函数式接口

相同点:

  • 都被@FunctionalInterface修饰
  • 都有一个抽象方法(apply)

不同点:

  • 接口定义的泛型不同
  • 抽象方法的参数与返回值数量不同

函数式接口的定义

从两个函数式接口的相同点来看,函数式接口的特点已经非常明显了,下面来看函数式接口的定义:

被@FunctionalInterface修饰,有且只有一个抽象方法的接口,接口中可以包含其他的方法,包括默认方法,静态方法,私有方法。

函数式接口的作用

函数式接口最重要的作用就是:将方法作为参数被传递。

直接上代码:

public static void testConsumer(Object input, Consumer<String> f) {
        f.accept(input.toString());
}
​
public static void main(String[] args) {
    Person person = new Person();
    //调用
    testConsumer(new StringBuffer("test"), System.out::println);
    testConsumer(new StringBuffer("Tom"),person::setName);
}

testConsumer方法可以将input.toString()传递给方法f作为参数被执行,而方法f只要是一个没有返回值,传入参数为一个String的方法即可,String具体如何执行对方法testConsumer来说不重要。

方法的参数是有类型的如String、int、Object...方法作为参数也一样,在js中所有方法被归于Function类中,方法被作为参数被传递时无需关心方法的参数返回值和具体定义。

而java中,各种方法根据参数和返回值进行“分类”:

Java内置四大核心

image-20220224154747445.png

其他常用接口

image-20220224154932481.png

lambda表达式

lambda 表达式的语法格式如下:

(parameters) -> expression 
或
(parameters) ->{ statements; }
​
(参数1,参数2) -> (表达式1;表达式2;表达式3;)

以下是lambda表达式的重要特征:

  • 可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号: 如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

那么lambda与函数式编程的关系是什么呢?来看下面的代码:

声明一个消费者:

 Consumer<Person> personConsumer1 = new Consumer<Person>() {
            @Override
            public void accept(Person t) {
                t.setName("Tom");
            }
        };

这么多行代码,有用的实际上只有第四行。

再看相同效果的lambda写法:

Consumer<Person> personConsumer2 = (e) -> {e.setName("Tom");};
Consumer<Person> personConsumer3 = e -> e.setName("Tom");

明显的简洁了。

Person排序实例

我们需要将personList排序,排序规则是area为New York的排在前;为Washington排在后(area的值只能取这两个);

首先是常规写法:

public static List<Person> sortPerson(List<Person> personList) {
        LinkedList<Person> rtn = new LinkedList();
        for (Person person : personList) {
            if("New York".equals(person.getArea())) {
                rtn.addFirst(person);
            }
            else {
                rtn.add(person);
            }
        }
        return rtn;
    }

然后是结合了函数式接口的写法:

//Comparator是比较器接口,传入两个同类型数据,返回正数则参数一大于参数二,
//返回负数则表示参数二大于参数一,返回0则表示两个数据相等
Comparator<Person> personComparator = new Comparator<Person>()
        {
            @Override
            public int compare(Person o1, Person o2) {
                if ("New York".equals(o1.getArea())&&"Washington".equals(o2.getArea())) {
                    return 1;
                }
                else if ("Washington".equals(o1.getArea())&&"New York".equals(o2.getArea())) {
                    return -1;
                }
                else {
                    return 0;
                }
            }
        };
        personList.sort(personComparator);

这怎么比常规写法还复杂,下面加上lambda表达式:

personList.sort((o1,o2) -> {
            if ("New York".equals(o1.getArea())&&"Washington".equals(o2.getArea())) {
                return 1;
            }
            else if ("Washington".equals(o1.getArea())&&"New York".equals(o2.getArea())) {
                return -1;
            }
            else {
                return 0;
            }
        });

如果再加上三元运算符:

personList.sort((o1,o2) -> {
           return o1.getArea().equals(o2.getArea()) ? 0 : ("New York".equals(o1.getArea()) ? 1 : -1);
        });

基本上算是一行代码完成了排序;