java 8新特性 常用内置函数式接口

内置函数式接口来由来

我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。

函数式接口在Java中是指:有且仅有一个抽象方法的接口。

importjava.util.List;

publicclassDemo01UserFunctionalInterface{

publicstaticvoidmain(String[]args) {

// 调用函数式接口中的方法

method((arr)->{

intsum=0;

for(intn:arr) {

    sum+=n;

       }

returnsum;

   });

  }

// 使用自定义的函数式接口作为方法参数

publicstaticvoidmethod(Operatorop) {

int[]arr={1,2,3,4};

intsum=op.getSum(arr);

System.out.println("sum = "+sum);

   }

}

@FunctionalInterface

interfaceOperator{

    publicabstractintgetSum(int[]arr);

}

常用内置函数式接口介绍

它们主要在 java.util.function 包中。下面是最常用的几个接口。

Supplier接口

@FunctionalInterface

publicinterfaceSupplier<T>{

    publicabstractTget();

}

Consumer接口

@FunctionalInterface

publicinterfaceConsumer<T>{

    publicabstractvoidaccept(Tt);

}

Function接口

@FunctionalInterface

publicinterfaceFunction<T,R>{

    publicabstractRapply(Tt);

}

Predicate接口

@FunctionalInterface

publicinterfacePredicate<T>{

    publicabstractbooleantest(Tt);

}

Predicate接口用于做判断,返回boolean类型的值

Supplier接口

java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类 型的对象数据。

@FunctionalInterface

publicinterfaceSupplier<T>{

    publicabstractTget();

}

使用Lambda表达式返回数组元素最大值

使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。

代码示例:

public class Demo05Supplier {

public static void main(String[] args) {

   printMax(() -> {

       int[] arr = {10, 20, 100, 30, 40, 50};

       // 先排序,最后就是最大的

       Arrays.sort(arr);

       return arr[arr.length - 1]; // 最后就是最大的

    });

}

private static void printMax(Supplier<Integer> supplier) {

   int max = supplier.get();

   System.out.println("max = " + max);

  }

}

Consumer接口

java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛 型参数决定。

@FunctionalInterface

publicinterfaceConsumer<T>{

publicabstractvoidaccept(Tt);

}

使用Lambda表达式将一个字符串转成大写和小写的字符串

Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:

public class Demo06Consumer {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

System.out.println(s.toLowerCase());

});

}

public static void test(Consumer consumer) {

consumer.accept("HelloWorld");

}

}

默认方法:andThen

如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操 作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代 码:

default Consumer andThen(Consumer after) {

Objects.requireNonNull(after);

return (T t) -> { accept(t); after.accept(t); };

}

[备注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出] 

[NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。] 

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合 的情况:

public class Demo07ConsumerAndThen {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

System.out.println(s.toLowerCase());

}, (String s) -> {

System.out.println(s.toUpperCase());

});

// Lambda表达式简写

test(s -> System.out.println(s.toLowerCase()), s ->

System.out.println(s.toUpperCase()));

}

public static void test(Consumer c1, Consumer c2) {

String str = "Hello World";

// c1.accept(str); // 转小写

// c2.accept(str); // 转大写

// c1.andThen(c2).accept(str);

c2.andThen(c1).accept(str);

}

}

运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组 合。

Function接口

java.util.function.Function 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。有参数有返回值。

@FunctionalInterface

public interface Function {

public abstract R apply(T t);

}

使用Lambda表达式将字符串转成数字

Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景 例如:将 String 类型转换为 Integer 类型。

public class Demo08Function {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

return Integer.parseInt(s); // 10

});

}

public static void test(Function function) {

Integer in = function.apply("10");

System.out.println("in: " + (in + 5));

}

}

默认方法:andThen

Function 接口中有一个默认的 andThen 方法,用来进行组合操作。JDK源代码如:

default Function andThen(Function after) {

Objects.requireNonNull(after);

return (T t) -> after.apply(apply(t));

}

该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:

public class Demo09FunctionAndThen {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

return Integer.parseInt(s);

}, (Integer i) -> {

return i * 10;

});

}

public static void test(Function f1, Function f2) {

// Integer in = f1.apply("66"); // 将字符串解析成为int数字

// Integer in2 = f2.apply(in);// 将上一步的int数字乘以10

Integer in3 = f1.andThen(f2).apply("66");

System.out.println("in3: " + in3); // 660

}

}

第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一 起。

[请注意,Function的前置条件泛型和后置条件泛型可以相同。] 

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。

@FunctionalInterface

public interface Predicate {

public abstract boolean test(T t);

}

Predicate接口用于做判断,返回boolean类型的值

使用Lambda判断一个人名如果超过3个字就认为是很长的名字

对test方法的参数T进行判断,返回boolean类型的结果。用于条件判断的场景:

public class Demo10Predicate {

public static void main(String[] args) {

test(s -> s.length() > 3, "迪丽热巴");

}

private static void test(Predicate predicate, String str) {

boolean veryLong = predicate.test(str);

System.out.println("名字很长吗:" + veryLong);

}

}

条件判断的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长。

默认方法:and

是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。其JDK源码为:

default Predicate and(Predicate other) {

Objects.requireNonNull(other);

return (t) -> test(t) && other.test(t);

}

使用Lambda表达式判断一个字符串中即包含W,也包含H

使用Lambda表达式判断一个字符串中包含W或者包含H

使用Lambda表达式判断一个字符串中即不包含W

如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:

public class Demo10Predicate_And_Or_Negate {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

return s.contains("H");

}, (String s) -> {

return s.contains("W");

});

}

public static void test(Predicate p1, Predicate p2) {

String str = "HelloWorld";

boolean b1 = p1.test(str); // 判断包含大写“H”

boolean b2 = p2.test(str); // 判断包含大写“W”

// if (b1 && b2) {

// System.out.println("即包含W,也包含H");

// }

boolean bb = p1.and(p2).test(str);

if (bb) {

System.out.println("即包含W,也包含H");

}

}

}

默认方法:or

使用Lambda表达式判断一个字符串中包含W或者包含H

与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:

default Predicate or(Predicate other) {

Objects.requireNonNull(other);

return (t) -> test(t) || other.test(t);

}

如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:

public class Demo10Predicate_And_Or_Negate {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

return s.contains("H");

}, (String s) -> {

return s.contains("W");

});

}

public static void test(Predicate p1, Predicate p2) {

String str = "HelloWorld";

boolean b1 = p1.test(str); // 判断包含大写“H”

boolean b2 = p2.test(str); // 判断包含大写“W”

// if (b1 || b2) {

// System.out.println("有H,或者W");

// }

boolean bbb = p1.or(p2).test(str);

if (bbb) {

System.out.println("有H,或者W");

}

}

}

默认方法:negate

使用Lambda表达式判断一个字符串中即不包含W

“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为:

default Predicate negate() {

return (t) -> !test(t);

}

从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前调 用 negate 方法,正如 and 和 or 方法一样:

import java.util.function.Predicate;

public class Demo10Predicate_And_Or_Negate {

public static void main(String[] args) {

// Lambda表达式

test((String s) -> {

return s.contains("H");

}, (String s) -> {

return s.contains("W");

});

}

public static void test(Predicate p1, Predicate p2) {

String str = "HelloWorld";

boolean b1 = p1.test(str); // 判断包含大写“H”

boolean b2 = p2.test(str); // 判断包含大写“W”

// 没有H,就打印

// if (!b1) {

// System.out.println("没有H");

// }

boolean test = p1.negate().test(str);

if (test) {

System.out.println("没有H");

}

}

}

小结

Supplier接口

@FunctionalInterface

public interface Supplier {

public abstract T get();

}

Consumer接口

@FunctionalInterface

public interface Consumer {

public abstract void accept(T t);

}

Consumer接口

@FunctionalInterface

public interface Function {

public abstract R apply(T t);

}

Predicate接口

@FunctionalInterface

public interface Predicate {

public abstract boolean test(T t);

}

Predicate接口用于做判断,返回boolean类型的值

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355

推荐阅读更多精彩内容