Java8 中的函数式方法接口实例
通过最近写java8 新特性教程,让我对java8中的一些特性和方法又有的新的理解,之前不清楚的地方豁然开朗,同时有些读者私信给我,说对于java8中的有些方法晦涩难懂,可能简单的方法会使用了,但是在实际的工作中会遇到各种的数据处理,那个时候就不知道该用那个接口来处理,对于这种问题,这对于初学者来说都是一个普遍的问题,作者在16 刚开始接触 Java8 的时候同样是出现这种问题,要想解决这个问题无非就是记住我下面的两条建议
- 理解函数式接口的用法和概念
- 对于Java 本身的几种基本的函数式接口理解彻底,并做到举一反三
- 多写代码,多练习
谨记以上三个建议,慢慢的你们就能在工作中得心应手的使用Java8的新特性了,下面我就给大家总结一些基本的函数式接口,以及这些函数式接口使用实例以及技巧,我写的这些实例,可以覆盖工作中90%的使用场景.
1. Java内置的基本六个函数接口
| 接口名称 | 内置方法 | 实例 |
|---|---|---|
UnaryOperator<T> | T apply(T t) | String::toLowerCase,Math::tan |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add,Math::pow |
Function<T, R> | R apply(T t) | Arrays::asList,Integer::toBinaryString |
Predicate<T, U> | boolean test(T t, U u) | String::isEmpty,Character::isDigit |
Supplier<T> | T get() | LocalDate::now,Instant::now |
Consumer<T> | void accept(T t) | System.out::println,Error::printStackTrace |
以上就是Java 内置的六种基本的函数式接口,下面我会针对不同的方法,提供不同的实例
1.1 Function 方法实例
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
他接受一个参数(参数类型是T),并且返回一个类型为 R 的对象,输入的参数类型和输出的参数类型可以时不一样的,也可以时一样的
1.2 Function<T,R>
编写一个传入一个String 类型的数据,并返回一个Integer 类型的数据
public static void main(String[] args) {
Function<String, Integer> function = (String x) -> x.length();
// 以上lambda 表达式可以写成 方法引用的方式
//Function<String, Integer> function = String::length;
Integer integer = function.apply("abc");
}
1.3 Function<T,R> 函数链
Function<T,R> 类型的函数 可以当做链使用,Function 内部有三种方法,
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
/**
* 从方法源码中可以推算出,先执行before 方法的 apply 方法,然后执行 接口的apply 方法
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 从方法源码中可以推算出,先执行接口中Apply 方法,然后执行after方法的apply方法
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 从方法中可以看出,返回值 和 传入的参数是同一个
*/
static <T> Function<T, T> identity() {
return t -> t;
}
实例
Function<String, Integer> function1 = String::length;
// Integer integer = function.apply("abc");
Function<Integer, Integer> function2 = x -> x * 2;
Function<String, String> function3 = x -> x + x;
Integer abc = function1.andThen(function2).apply("abc");
System.out.println(abc);
Integer abc1 = function1.compose(function3).apply("abc");
System.out.println(abc1);
有些同学可能会问,为什么 例子中 compose 方法中放到是 function3 而不是 function2?
对于提出这个问题的同学,我可能要批评你了, 首先你没有理解这两个方法的意思,然后你自己没有动手敲一敲这些代码,通过idea 编译器,你放入function2的话编译器会报错的,,至于为什么报错,这个问题,就交给同学们自己解答了,,如果自己找到问题,那么恭喜你,,你已经完全掌握这几个方法了.
1.3 通过List 转Map
先上代码
public static void main(String[] args) {
/*
* 从这个集合 转换成一个,元素为key, 元素长度为Value 的map
* */
List<String> list = Arrays.asList("node", "c++", "java", "javascript");
Map<String, Integer> collect = list.stream().collect(Collectors.toMap(x ->x,String::length);
//Map<String, Integer> collect = list.stream().collect(Collectors.toMap(x -> x, //String::length, (k1, k2) -> k2));
List<String> list2 = Arrays.asList("node", "c++", "java", "javascript","java");
}
以上代码就是将一个List 转成Map 的写法, 但是当我修改了List 这个集合后,这样写法就不对了.我们在list2 这个集合中增加了一个相同的元素java,试试运行我们刚才的代码
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 4
运行报错,错误信息显示第四个索引位置的Key 重复, 第四个索引不就是我们刚 加入的 元素java 吗? 我们知道 map 这个集合的 key 是唯一的,当后面的key 和前面的key 重复的时候,后面的数据会覆盖前面的数据,,同样java8 中也提供了 覆盖key的写法
Map<String, Integer> collect = list.stream().collect(Collectors.toMap(x -> x,String::length,(newValue,oldValue)->oldValue));
这句代码的意思是,当发生主键重复的时候,oldVlaue 不被替换 ,,当我们将oldVlaue 换成newValue 的时候意思是 当发生key 重复的时候,新的值 会替换老的值.
2. BiFunction 方法实例
2.1 通过源码我们知道接口方法,默认接收两个参数,返回一个参数,
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
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));
}
}
先上代码
public static void main(String[] args) {
// takes two Integers and return an Integer
BiFunction<Integer, Integer, Integer> func = (x1, x2) -> x1 + x2;
Integer result = func.apply(2, 3);
System.out.println(result); // 5
// take two Integers and return an Double
BiFunction<Integer, Integer, Double> func2 = (x1, x2) -> Math.pow(x1, x2);
Double result2 = func2.apply(2, 4);
System.out.println(result2); // 16.0
// take two Integers and return a List<Integer>
BiFunction<Integer, Integer, List<Integer>> func3 = (x1, x2) -> Arrays.asList(x1 + x2);
List<Integer> result3 = func3.apply(2, 3);
System.out.println(result3);
}
2.2 BiFunction < t,u,r > + Function < t,r >
BiFunction 和 Function 组成链进行使用
public static void main(String[] args) {
// 传入一个
BiFunction<Integer, Integer, Double> func1 = (a1, a2) -> Math.pow(a1, a2);
// takes Double, returns String
Function<Double, String> func2 = (input) -> "Result : " + String.valueOf(input);
String result = func1.andThen(func2).apply(2, 4);
System.out.println(result);
}
同样我们可以自己封装成一个转换方法,更加方便我们使用
public static void main(String[] args) {
String result = powToString(2, 4,
(a1, a2) -> Math.pow(a1, a2),
(r) -> "Result : " + String.valueOf(r));
System.out.println(result); // Result : 16.0
}
// 这个方法 的意思是 func 方法先接收a1,a2两个参数,调用apply 方法,然后将返回的Double 数值,在传入func2 方法中
public static <R> R powToString(Integer a1, Integer a2,
BiFunction<Integer, Integer, Double> func,
Function<Double, R> func2) {
return func.andThen(func2).apply(a1, a2);
}
我们可以将上述的放进继续修改成更加通用的一个方法
public static <A1, A2, R1, R2> R2 convert(A1 a1, A2 a2,
BiFunction<A1, A2, R1> func,
Function<R1, R2> func2) {
return func.andThen(func2).apply(a1, a2);
}
这个就是通用的方法, 只要符合参数规范都可以使用这个 方法
3. BinaryOperator 方法实例
BinaryOperator 同样是一个函数式接口,它继承扩展了BiFunction,BinaryOperator 接受两个相同的参数,并返回相同类型参数的结果. 接受参数和返回参数都必须是相同的,这个也是 二进制操作与其他方法最大的区别.
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
// 扩展的方法
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
// 扩展的方法
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
3.1
可以替换BiFunction使用
public static void main(String[] args) {
// BiFunction
BiFunction<Integer, Integer, Integer> func = (x1, x2) -> x1 + x2;
Integer result = func.apply(2, 3);
System.out.println(result); // 5
// BinaryOperator
BinaryOperator<Integer> func2 = (x1, x2) -> x1 + x2;
Integer result2 = func.apply(2, 3);
System.out.println(result2); // 5
}
3.2 IntBinaryOperator , longBinaryOperator, doubleBinaryOperator
如果我们操作的数据都是 int 类型 ,long 类型,double 类型的话,可以 用专门的函数式接口来处理
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int result = math((numbers), 0, (a, b) -> a + b);
System.out.println(result); // 55
int result2 = math((numbers), 0, Integer::sum);
System.out.println(result2); // 55
IntStream
}
public static int math(int[] list, int init, IntBinaryOperator accumulator) {
int result = init;
for (int t : list) {
result = accumulator.applyAsInt(result, t);
}
return result;
}
4. UnaryOperator 方式实例
在java8 中 UnaryOperator是一个函数式接口,它扩展了Function 接口, 这个接口接收一个参数,并返回一个相同类型的参数,Function中的使用方式,UnaryOperator都可以使用.
直接看代码
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> result = math(list, x -> x * 2);
System.out.println(result); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
}
public static <T> List<T> math(List<T> list, UnaryOperator<T> uo) {
List<T> result = new ArrayList<>();
for (T t : list) {
result.add(uo.apply(t));
}
return result;
}
5. Predicate (谓词)
predicate 函数式接口, 他接受一个参数并返回一个boolean 值,长用于我们代码中的判断,先看下源码
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
从接口中方法名字,我们基本上都知道这些方法的意思,所以这里我就不做解释了,,不理解的可以敲一下我写的代码,验证
5.1 filter(Predicate predicate)
这个是stream 中的方法,这个方法接收一个predicate方法 作为参数
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> collect = list.stream().filter(x -> x > 5).collect(Collectors.toList());
System.out.println(collect); // [6, 7, 8, 9, 10]
}
以上代码是一个直接写法,我们可以吧predicate 方法单独定义出来
public static void main(String[] args) {
Predicate<Integer> noGreaterThan5 = x -> x > 5;
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> collect = list.stream()
.filter(noGreaterThan5)
.collect(Collectors.toList());
System.out.println(collect); // [6, 7, 8, 9, 10]
}
5.2 多个过滤器使用
predicate.and()
// java8之前的写法
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// multiple filters
List<Integer> collect = list.stream()
.filter(x -> x > 5 && x < 8).collect(Collectors.toList());
System.out.println(collect);
}
// java8之后的写法
public static void main(String[] args) {
Predicate<Integer> noGreaterThan5 = x -> x > 5;
Predicate<Integer> noLessThan8 = x -> x < 8;
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> collect = list.stream()
.filter(noGreaterThan5.and(noLessThan8))
.collect(Collectors.toList());
System.out.println(collect);
}
同理 || 符合可以用or() 替换, != 可以用 negate () 替换
我们还可以将多个 predicate链接起来使用
public static void main(String[] args) {
Predicate<String> startWithA = x -> x.startsWith("a");
// start with "a" or "m"
boolean result = startWithA.or(x -> x.startsWith("m")).test("mkyong");
System.out.println(result); // true
// !(start with "a" and length is 3)
boolean result2 = startWithA.and(x -> x.length() == 3).negate().test("abc");
System.out.println(result2); // false
}
6 . BiPredicate
Bipredicate是一个函数式接口,基本上用法和 Predicate 是一致的,区别在于Bipredicate 接受两个参数,
- 实例1
public static void main(String[] args) {
BiPredicate<String, Integer> filter = (x, y) -> {
return x.length() == y;
};
boolean result = filter.test("mkyong", 6);
System.out.println(result); // true
boolean result2 = filter.test("java", 10);
System.out.println(result2); // false
}
-
实例2 predicate 作为一个参数传入
public class JavaBiPredicate2 { public static void main(String[] args) { List<Domain> domains = Arrays.asList(new Domain("google.com", 1), new Domain("i-am-spammer.com", 10), new Domain("baidu.com", 0), new Domain("microsoft.com", 2)); BiPredicate<String, Integer> bi = (domain, score) -> { return (domain.equalsIgnoreCase("google.com") || score == 0); }; // if google or score == 0 List<Domain> result = filterBadDomain(domains, bi); System.out.println(result); // // if score == 0 List<Domain> result2 = filterBadDomain(domains, (domain, score) -> score == 0); System.out.println(result2); // mkyong.com, microsoft.com // if start with i or score > 5 List<Domain> result3 = filterBadDomain(domains, (domain, score) -> domain.startsWith("i") && score > 5); System.out.println(result3); // // chaining with or List<Domain> result4 = filterBadDomain(domains, bi.or( (domain, x) -> domain.equalsIgnoreCase("microsoft.com")) ); System.out.println(result4); // } public static <T extends Domain> List<T> filterBadDomain( List<T> list, BiPredicate<String, Integer> biPredicate) { return list.stream() .filter(x -> biPredicate.test(x.getName(), x.getScore())) .collect(Collectors.toList()); } } class Domain { String name; Integer score; public Domain(String name, Integer score) { this.name = name; this.score = score; } // getters , setters , toString }
7 . Consumer (消费者)
Consumer 是一个函数式接口,他接受一个参数,但是不返回任何值,我们可以看做是终端操作
public static void main(String[] args) {
Consumer<String> print = x -> System.out.println(x);
print.accept("java"); // java
}
以上是一个最简单的 消费者接口
7.1 接受一个 consumer 接口作为参数
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// implementation of the Consumer's accept methods.
Consumer<Integer> consumer = (Integer x) -> System.out.println(x);
forEach(list, consumer);
// or call this directly
forEach(list, (Integer x) -> System.out.println(x));
}
static <T> void forEach(List<T> list, Consumer<T> consumer) {
for (T t : list) {
consumer.accept(t);
}
}
7.2 BiConsumer
BiConsumer 是Consumer 接口函数的扩展,与其他Bi 开头的其他函数式接口一样,BiConsumer 函数式接口接受两个参数, 而且不返回结果
public static void main(String[] args) {
BiConsumer<Integer, Integer> addTwo = (x, y) -> System.out.println(x + y);
addTwo.accept(1, 2); // 3
}
8.supplier (提供者)
java8 中,Supplier 是一个函数式接口,它不接受参数并返回结果
@FunctionalInterface
public interface Supplier<T> {
T get();
}
8.1 通过Supplier 返回当前日期时间
public class Java8Supplier1 {
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
Supplier<LocalDateTime> s = () -> LocalDateTime.now();
LocalDateTime time = s.get();
System.out.println(time);
Supplier<String> s1 = () -> dtf.format(LocalDateTime.now());
String time2 = s1.get();
System.out.println(time2);
}
}
8.2 返回一个 Supplier
ublic class Java8Supplier2<T> {
public static void main(String[] args) {
Java8Supplier2<String> obj = new Java8Supplier2();
List<String> list = obj.supplier().get();
}
public Supplier<List<T>> supplier() {
// lambda
// return () -> new ArrayList<>();
// constructor reference
return ArrayList::new;
}
8.3 返回一个Developer 工厂
public class Java8Supplier3 {
public static void main(String[] args) {
Developer obj = factory(Developer::new);
System.out.println(obj);
Developer obj2 = factory(() -> new Developer("mkyong"));
System.out.println(obj2);
}
public static Developer factory(Supplier<? extends Developer> s) {
Developer developer = s.get();
if (developer.getName() == null || "".equals(developer.getName())) {
developer.setName("default");
}
developer.setSalary(BigDecimal.ONE);
developer.setStart(LocalDate.of(2017, 8, 8));
return developer;
}
}
public class Developer {
String name;
BigDecimal salary;
LocalDate start;
// for factory(Developer::new);
public Developer() {
}
// for factory(() -> new Developer("mkyong"));
public Developer(String name) {
this.name = name;
}
// get, set, constructor, toString
//...
}