引言

使用Java8开发也有年头了,但是对于新特性没有深入过了解,只是在使用一些框架的时候会用到,如在使用mybatis-plus的时候都是写Lambda语句,感觉还是很爽的,其他的像LocalDateTime还是生成数据库实体类才发现的.😭 真是泪目啊,自己太菜了! 所以最近打算系统的学习这些新特性 💪

函数式接口

函数式接口(Functional Interface)就是有且仅有一个抽象方法(public abstract),但可以拥有多个非抽象方法的接口(default),如下:

/**
 * 函数式接口
 */
interface IntInterface {
    
    int doubleNum(int i);

    /**
     * 默认实现方法
     */
    default int add(int x, int y) {
        return x + y;
    }

    default boolean min(int x, int y) {
        return x > y;
    }
}

那么如何去实现这个接口呢?

  public static void main(String[] args) {
        IntInterface i1 = i -> i * 2;
	//指定参数类型
        IntInterface i2 = (int i) -> i * 2;

        IntInterface i3 = i -> {
            System.out.println("i: " + i);
            return i * 2;
        };

        IntInterface i4 = (i) -> i * 2;
    }

上述代码左边i 表示函数式接口抽象方法所需要的参数,箭头右边表示Lambda返回的表达式,即函数体.

@FunctionalInterface

@FunctionalInterface这个注解也是Java1.8新推出了,用于标注函数式接口.
image.png
标注了该注解后,如果再添加抽象方法,则会无法编译.这里有一个注意事项是函数式接口中的抽象方法不能是Object的public方法.
在JDK8之前我们创建函数对象都是使用的匿名内部类方式,如下:

 /*匿名内部类*/
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "-- 运行");
            }
        });
        //lambda传参
        startThread(() -> {
            System.out.println(Thread.currentThread().getName() + "-- 运行");
        });
        startThread(() -> System.out.println(Thread.currentThread().getName() + "-- 运行"));

下面来展示下使用传统匿名内部类实现集合排序到使用Lambda表达式创建接口实例完成集合排序的代码:

 List<String> words = new ArrayList<>();
 //使用匿名内部类
 Collections.sort(words, new Comparator<String>() {
      @Override
      public int compare(String o1, String o2) {
          return Integer.compare(o1.length(), o2.length());
         }
     });

//使用Lambda表达式实现Comparator接口
Collections.sort(words, (o1, o2) -> Integer.compare(o1.length(), o2.length()));

//使用List在JDK1.8新增的sort(Comparator<? super E> c)方法
words.sort(Comparator.comparingInt(String::length));

这样做的好处是代码更加简洁,程序变得更加清晰.

方法引用

Java8的方法引用由类名或对象名加上::,后面跟上方法名称组成,如Integer::parseInt
方法引用只需要参数类型和返回值类型相同即可,下面定义一个函数式接口:

interface ShowApi {
    void call(String s);
}

再定义一个普通类:

class Describe {
    public void show(String s) {
        System.out.println("Describe 's show: " + s);
    }

    public void call(String msg) {
        System.out.println("Describe 's call: " + msg);
    }
}

现在写一下如何将ShowApi的call方法映射到Describe的方法上

// ShowApi api = Describe::call; 此处报错,Describe::call不能在没有Describe对象的前提下调用call,
表示未绑定的方法引用,因为它尚未“绑定”到对象, 需要创建Describe对象的实例或者call方法为static静态方法.
Describe describe = new Describe();
//参数类型一致,返回值类型相同
//对象名 : 实例
  ShowApi show = describe::show;
  ShowApi show2 = describe::call;
  show.call("instance reference");

只要是引用方法的参数类型和返回类型一致即可,上面的例子称为绑定方法引用,即对已实例化对象的方法的引用.