java 自定义函数接口,1. Lambda、JDK8接口使用、自定义函数接口

JDK8入门

JDK8默认最大的特性应该就是Lambda表达式了吧。先上线几个Lambda表达式进行体验一下。

代码我托管于GitHub社区:https://github.com/WeidanLi/Java-jdk8-demo

准备

为了测试,我们新建一个苹果,围绕着苹果开来展开需求的实现。苹果具有两个属性,一个颜色和一个重量。我们可以通过集合+我们自己的POJO类来实现。

实例是JDK8实战中的例子,如有侵犯,请告知删除。

package cn.liweidan.jdk8.pojo;

/**

*

Desciption:

* CreateTime : 2017/6/2 下午2:55

* Author : Weidan

* Version : V1.0

*/

public class Apple {

private String color;

private int wight;

public Apple(String color, int wight) {

this.color = color;

this.wight = wight;

}

public Apple() {

}

public Apple(int wight) {

this.wight = wight;

}

public String getColor() {

return color;

}

public void setColor(String color) {

this.color = color;

}

public int getWight() {

return wight;

}

public void setWight(int wight) {

this.wight = wight;

}

@Override

public String toString() {

return "Apple{" +

"color='" + color + '\'' +

", wight=" + wight +

'}';

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Apple apple = (Apple) o;

if (wight != apple.wight) return false;

return color != null ? color.equals(apple.color) : apple.color == null;

}

@Override

public int hashCode() {

int result = color != null ? color.hashCode() : 0;

result = 31 * result + wight;

return result;

}

}

一、Predicate接口

Predicate接口中定义了一个需要实现的方法test(T t)方法,还有JDK8特有的几个已经实现了的默认方法,打开Predicate里有一个注解,@FunctionalInterface,代表这个接口只能拥有一个未实现的方法,是一个函数接口。

现在,我们需要在我们的一个苹果集合中,拿出重量大于100的苹果。

如果是以前,那么我们就需要通过遍历一个集合,拿出来符合条件的苹果,然后进行返回。

public static List filterApple(List appleList){

List apples = new ArrayList<>();

for (Apple apple : appleList) {

if(apple.getWight() > 100){

apples.add(apple);

}

}

return apples;

}

这种方式,可以实现现在的需求,但是当用户的需求改了以后,比如找出红色的苹果,那我们就需要重写里面的if方法的条件。不够灵活。

JDK8引入Predicate接口以后,我们就可以通过把这个条件进行封装,把经常改变需求的if里面的判断语句进行封装,变成Predicate接口,在调用filterApple的时候将条件传入,即可实现需求的变更。

JDK8的方法可以这么写:

/**

Predicate接口:默认调用test方法,返回boolean值,传递的参数只有一个。

在需要调用Lambeda表达式返回boolean的时候使用。

*/

public static List filterApple(List appleList, Predicate predicate){

List apples = new ArrayList<>();

for (Apple apple : appleList) {

if(predicate.test(apple)){

apples.add(apple);

}

}

return apples;

}

Predicate即可通过Lambda表达式传入需要的条件,即可实现可变的过滤需求。

public static void main(String[] args) {

List appleList = Arrays.asList(new Apple("RED", 80),

new Apple("GREEN", 100),

new Apple("BLACK", 150));

// 重量大于100

List filterApple = filterApple(appleList, apple -> apple.getWight() > 100);

// 红色的苹果

filterApple = filterApple(appleList, apple -> apple.getColor().equals("RED"));

}

可以看到,我们已经可以把我们需要的苹果筛选出来了。

这个方法的调用,就是通过传入一个Predicate实现,然后把apple.getWight() > 100返回给test(Apple apple)方法,test方法再进行返回给if语句,从而拿到我们需要的苹果,是不是比以前的方法简单。

所以当我们有个需求是需要可变的返回值boolean得需求的时候,即可使用Predicate接口。后面的JDK8的集合流中的filter()也是通过传入Predicate拿到boolean值进行筛选的。

代码位置:cn.liweidan.jdk8.PredicateInterface中

二、Consumer接口

Consumer接口也是个函数式接口,只有该接口只含有一个未实现的accept(T t)方法。返回值是void,用于封装在日常生活中没有返回值情况的代码,比如遍历一个集合并且打印每个元素,我们即可通过Consumer接口实现一个forEach方法。接收一个集合并且使用Lambda书写需要对集合的操作,在Consumer参数中即可拿到每一个集合中的元素进行操作。

/**

* Consumer提供了一个accept方法,返回void类型。

*/

public static void forEach(List list, Consumer s){

for (T t : list) {

s.accept(t);

}

}

调用该forEach方法:

@Test

public void test01(){

forEach(Arrays.asList("Lambde", "test", "stream"),

s -> {

System.out.println(s);

});

}

Consumer意为消费者,在上面例子中,即拿到集合中所有的元素进行消费。

代码位置:cn.liweidan.jdk8.ConsumerInterface

三、Function接口

Function就好玩了,可以自定义传入的类型以及传出的类型。T表示传入的类型,R表示传出的类型。

Function接口只有一个未实现的R apply(T t)方法,用于传入一个T类型的值,而传出一个R类型的值。比如我们现在需要把一组字符串的长度全部输出出来。在以往我们就需要通过遍历所有的元素,然后用一个集合去封装,在遍历中一个一个取出长度放入我们新建的集合当中去,然后将这个集合进行返回。

然而使用Function方法的时候,我们就可以很自由的进行取值。

现在我们来写一个map方法,用于取出集合中元素某一个属性的所有值并且进行返回。

/**

* Function, T表示传入的类型,R表示返回的类型。即传入T类型的参数,返回R类型的参数

* @param list

* @param function

* @param

* @param

* @return

*/

public static List map(List list, Function function){

List result = new ArrayList<>();

for (T t : list) {

result.add(function.apply(t));

}

return result;

}

@Test

public void test01(){

List stringList = Arrays.asList("lambda", "test", "javascript");

List map = map(stringList, s -> s.length());

System.out.println(map);

}

代码位置:cn.liweidan.jdk8.FunctionDemo

运行的时候JDK8可以通过传入的集合的泛型是String属性,从而把String赋予给T,然后再根据我们需要取值的结果的属性值,在这里s.length()取出来的值是Integer类型的。所以R的类型就是Integer类型。把T和R带入Function中的两个泛型值,我们就可以发现其实就是和我们以前写的遍历取值是一样的,只不过是现在的方式可以更灵活。这里得益于Lambda表达式,通过() -> expression或者() -> {statement;}来表达我们需要的需求。

四、Lambda表达式

Lambda表达式的历史就不说了。这里说的是Lambda的格式,Lambda表达式的前半部分(Apple a, Apple b)表示传入给Lambda的参数值,也就是相当于我们方法中的参数。这个不难理解吧。然而后半部分就比较有讲究了。

后半部分{}或者直接书写"result",用于表示返回值。在这里需要注意的是{}和"result"的区别,{}表示书写Java语句,也就是我们日常写的代码,比如上面的forEach方法中,我们就是通过s -> {System.out.println(s);}来写Java代码的,但是如果我们直接返回值,就不需要写花括号,只需要直接把返回值放在后部分即可。如Predicate接口中的apple -> apple.getWight() > 100后半部分直接用返回值返回boolean值。像函数式接口都支持Lambda的书写方式。

比如:

() -> return "result";和() -> {"result"}均是错误的写法,应该写成() -> "result"或者() -> {return "result";}

对于Lambda表达式我是这么去理解的,前半部分就是传入的值,后半部分就是我们需要执行的语句,函数式接口就相当于把我们需要执行的语句放入Lambda的后半部分一样。

这时候我们就需要来一个例子看看了,并且讲解函数式接口。

五、自定义函数式接口。

我们现在有一个需求,我需要有个方法可以自定义我们读取文件的内容的方法,但是前后的try-catch均让代码帮我封装了。我只要关心我读取的代码就可以了。

这里就涉及到了环绕的函数式编程了,但是这并不难,我们先来看看传统的方法:

public static String processFileOld(BufferedReaderProcess b){

StringBuilder str = new StringBuilder();

try(BufferedReader br =

new BufferedReader(

new FileReader("/Users/liweidan/Java/workspace/Java-jdk8-demo/src/main/java/cn/liweidan/jdk8/Lambda/LambdaDemo01.java"))){

str.append(br.readLine());

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return str.toString();

}

这样可以实现读取指定文件的一行,但是当我们需要读取文件的第二行的时候怎么办,我们又需要修改这段代码。

所以我们应该把读取的内容给封装起来。首先我们先建一个函数式接口BufferedReaderProcess接收一个BufferedReader,然后对BufferedReader的动作进行操作。

package cn.liweidan.jdk8.Lambda.Demo02;

import java.io.BufferedReader;

import java.io.IOException;

/**

*

Desciption:读取流的函数式编程,把行为参数化

* CreateTime : 2017/6/2 下午3:33

* Author : Weidan

* Version : V1.0

*/

@FunctionalInterface // 注意这个注解

public interface BufferedReaderProcess {

String process(BufferedReader b) throws IOException;

}

@FunctionalInterface注解,用于只是该接口是函数式接口,如果没有该注释也可以编译通过,但是编译器就不能判断他只能拥有一个未实现的方法。

这时候我们可以编写一个方法,用于接收BufferedReaderProcess,前后包装流的常用动作,包括流的获取以及流的关闭,这里我们使用JDK7的新特性,自动关闭流。

@Test

public void test01(){

String br = LambdaDemo02.processFile(b -> b.readLine());

System.out.println(br);

br = LambdaDemo02.processFile(b -> b.readLine() + b.readLine() + b.readLine());

System.out.println(br);

}

/**

* 将读取文件的动作封装成参数

* @param b

* @return

*/

public static String processFile(BufferedReaderProcess b){

try(BufferedReader br =

new BufferedReader(

new FileReader("/Users/liweidan/Java/workspace/Java-jdk8-demo/src/main/java/cn/liweidan/jdk8/Lambda/LambdaDemo01.java"))){

return b.process(br);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return null;

}

代码位置:cn.liweidan.jdk8.Lambda.LambdaDemo02

六、Lambda的方法引用

当我们需要使用Lambda表达式去调用一个对象的方法的时候,可以通过::来调用,比如调用String的compareToIgnoreCase方法,可以写成String:: compareToIgnoreCase,又可以减少代码量了。方法调用可以针对构造方法、普通方法以及静态方法进行调用。下面给出几个需求的实现代码。

public class MethodQuoteDemo01 {

@Test

public void test(){

List list = Arrays.asList("a", "b", "A", "B");

list.sort(String::compareToIgnoreCase);// 调用compareToIgnoreCase进行比较

System.out.println(list);

}

}

package cn.liweidan.jdk8.MethodQuote;

import cn.liweidan.jdk8.pojo.Apple;

import org.junit.Test;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Comparator;

import java.util.List;

import java.util.function.Function;

import java.util.function.Supplier;

/**

*

Desciption:构造方法引用Demo

* CreateTime : 2017/6/2 下午7:46

* Author : Weidan

* Version : V1.0

*/

public class MethodQuoteDemo02 {

@Test

public void test01(){

/**

* 空构造器

*/

Supplier c1 = Apple::new;

Apple apple = c1.get();

System.out.println(apple);

}

@Test

public void test02(){

/**

* Apple存在只需要传递一个Integer参数的构造器

*/

Function c2 = Apple::new;

Apple apply = c2.apply(100);

System.out.println(apply);

}

/**

* 使用map批量创建指定重量的苹果

*/

@Test

public void test03(){

List integers = Arrays.asList(7, 3, 9, 10);

List appleList = map(integers, Apple::new);

System.out.println(appleList);

}

public static List map(List wights, Function function){

List res = new ArrayList<>();

for (T t : wights) {

res.add(function.apply(t));

}

return res;

}

/**

* 调用getWight对苹果进行按照重量进行排序

*/

@Test

public void test04(){

List appleList = Arrays.asList(new Apple("RED", 80),

new Apple("GREEN", 100),

new Apple("BLACK", 150));

appleList.sort(Comparator.comparing(Apple::getWight));

System.out.println(appleList);

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值