第一步:比如我们想要读取某个文件,那可以有如下方法:
public static String processFile() throws IOException { // Java7新增的语法,try(){},可自动关闭资源,减少了代码的臃肿 try( BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\JavaProject\\JavaBasicDemo\\test.txt"))){ return bufferedReader.readLine(); } }可以看到,核心的行为动作就是 return bufferedReader.readLine();,表示读取第一行的数据并返回
那如果我们想要读取两行呢?三行?
第二步:这时就需要用到上面的函数式接口了,下面就是我们自己编写的函数式接口
@FunctionalInterface interface FileReadInterface{ // 这里接受一个BufferedReader对象,返回一个String对象 String process(BufferedReader reader) throws IOException; }可以看到,只有一个抽象方法process() ,它就是用来处理第一步中的核心动作(读取文件内容)
至于想读取多少内容,那就需要我们在lambda表达式中定义了
第三步:接下来我们定义多个lambda表达式,用来传递给函数式接口,其中每个lambda表达式就代表了一种不同的行为,代码如下:
// 读取一行 FileReadInterface fileReadInterface = reader -> reader.readLine(); // 读取两行 FileReadInterface fileReadInterface2 = reader -> reader.readLine() + reader.readLine();第四步:我们需要修改第一步的processFile(),让其接受一个函数式接口,并调用其中的抽象方法,代码如下:
// 参数为第二步我们自己手写的函数式接口 public static String processFile(FileReadInterface fileReadInterface) throws IOException { try( BufferedReader bufferedReader = new BufferedReader(new FileReader("./test.txt"))){ // 这里我们不再自己定义行为,而是交给函数式接口的抽象方法来处理,然后通过lambda表达式的传入来实现多个行为 return fileReadInterface.process(bufferedReader); } }第五步:拼接后,完整代码如下:
public class FileReaderDemo { public static void main(String[] args) throws IOException { // 第三步: // lambda表达式1 传给 函数式接口:只读取一行 FileReadInterface fileReadInterface = reader -> reader.readLine(); // lambda表达式2 传给 函数式接口:只读取两行 FileReadInterface fileReadInterface2 = reader -> reader.readLine() + reader.readLine(); // 最后一步: 不同的函数式接口的实现,表现出不同的行为 String str1 = processFile(fileReadInterface); String str2 = processFile(fileReadInterface2); System.out.println(str1); System.out.println(str2); } // 第四步: 读取文件方法,接受函数式接口作为参数 public static String processFile(FileReadInterface fileReadInterface) throws IOException { try( BufferedReader bufferedReader = new BufferedReader(new FileReader("./test.txt"))){ // 调用函数式接口中的抽象方法来处理数据 return fileReadInterface.process(bufferedReader); } } // 第一步: public static String processFile() throws IOException { try( BufferedReader bufferedReader = new BufferedReader(new FileReader("./test.txt"))){ return bufferReader.readLine(); } } } // 第二步: 我们手写的函数式接口 @FunctionalInterface interface FileReadInterface{ String process(BufferedReader reader) throws IOException; }其实你会发现,我们手写的这个函数式接口,其实就是Function<T>去除泛型化后的接口,如下所示:
@FunctionalInterface public interface Function<T, R> { // 都是接受一个参数,返回另一个参数 R apply(T t); }下面我们列出Java中常用的一些函数式接口,你会发现自带的已经够用了,基本不会需要我们自己去写
这里的手写只是为了自己实现一遍,可以加深理解程度
6. 常用的函数式接口 7. 什么是方法引用我们先看一个例子
前面我们写的lambda表达式,其实还可以简化,比如
// 简化前 Function<Cat, Integer> function = c->c.getAge(); // 简化后 Function<Cat, Integer> function2 = Cat::getAge;其中简化后的Cat::getAge,我们就叫做方法引用
方法引用就是引用类或对象的方法;
下面我们列出方法引用的三种情况:
Object::instanceMethod(对象的实例方法)
Class::staticMethod(类的静态方法)
Class::instanceMethod(类的实例方法)
像我们上面举的例子就是第三种:类的实例方法
下面我们用代码演示上面的三种方法:
public class ReferenceDemo { public static void main(String[] args) { // 第一种:引用对象的实例方法 Cat cat = new Cat(1); Function<Cat, Integer> methodRef1 = cat::getSum; // 第二种:引用类的静态方法 Supplier<Integer> methodRef2 = Cat::getAverageAge; // 第三种:引用类的实例方法 Function<Cat, Integer> methodRef3 = Cat::getAge; } } class Cat { int age; public Cat(int age) { this.age = age; } // 获取猫的平均年龄 public static int getAverageAge(){ return 15; } // 获取两只猫的年龄总和 public int getSum(Cat cat){ return cat.getAge() + this.getAge(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }为啥要用这个方法引用呢?
方法引用好比lambda表达式的语法糖,语法更加简洁,清晰
一看就知道是调用哪个类或对象的哪个方法
8. 什么是构造引用上面介绍了方法引用,就是直接引用某个方法
这里的构造引用同理可得,就是引用某个类的构造方法
构造引用的表达式为:Class::new,仅此一种
如果你有多个构造函数,那编译器会自己进行推断参数(你看看,多好,多简洁)