函数式接口、默认方法和Optional类

函数式接口、默认方法和Optional类

函数式接口

函数式接口通过一个单一的功能来表现。例如,带有单个compareTo方法的比较接口,被用于比较的场合。Java 8 开始定义了大量的函数式接口来广泛地用于lambda表达式。

Java 8 引入的一个核心概念是函数式接口(Functional Interfaces)。通过在接口里面添加一个抽象方法,这些方法可以直接从接口中运行。如果一个接口定义唯一一个抽象方法,那么这个接口就成为函数式接口。同时,引入了一个新的注解:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。这个注解是非必须的,只要接口只包含一个方法的接口,虚拟机会自动判断,不过最好在接口上使用注解 @FunctionalInterface 进行声明。在接口中添加了 @FunctionalInterface 的接口,只允许有一个抽象方法,否则编译器也会报错。引用自IBM - Java 8 新特性概述

相关的接口及描述

下面是部分函数式接口的列表

接口 描述
BitConsumer<T,U> 改接口代表了接收两个输入参数T、U,并且没有返回的操作
BiFunction<T,U,R> 该接口代表提供接收两个参数T、U,并且产生一个结果R的方法
BinaryOperator<T> 代表了基于两个相同类型的操作数, 产生任然是相同类型结果的操作
BiPredicate<T,U> 代表了对两个参数的断言操作(基于Boolean值的方法)
BooleanSupplier 代表了一个给出Boolean值结果的方法
Consumer<T> 代表了接受单一输入参数并且没有返回值的操作
DoubleBinaryOperator 代表了基于两个Double类型操作数的操作,并且返回一个Double类型的返回值
DoubleConsumer 代表了一个接受单个Double类型的参数并且没有返回的操作
DoubleFunction<R> 代表了一个接受Double类型参数并且返回结果的方法
DoublePredicate 代表了对一个Double类型的参数的断言操作
DoubleSupplier 代表了一个给出Double类型值的方法
DoubleToIntFunction 代表了接受单个Double类型参数但返回Int类型结果的方法
DoubleToLongFunction 代表了接受单个Double类型参数但返回Long类型结果的方法
DoubleUnaryOperator 代表了基于单个Double类型操作数且产生Double类型结果的操作
Function<T,R> 代表了接受一个参数并且产生一个结果的方法
IntBinaryOperator 代表了对两个Int类型操作数的操作,并且产生一个Int类型的结果
IntConsumer 代表了接受单个Int类型参数的操作,没有返回结果
IntFunction<R> 代表了接受Int类型参数并且给出返回值的方法
IntPredicate 代表了对单个Int类型参数的断言操作

更多的接口可以参考Java官方API手册:java.lang.Annotation Type FunctionalInterface。在实际使用过程中,加有 @FunctionalInterface 注解的方法均是此类接口,位于java.util.Funtion包中。

一个函数式编程的例子

下面我们通过一个例子学习如何使用这些函数式编程的接口

新建一个类 NewFeaturesTester.java,以下是 NewFeaturesTester.java 类中应当输入的代码:

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class NewFeaturesTester {
   public static void main(String args[]){
      List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

      System.out.println("All of the numbers:");

      eval(list, n->true);

      System.out.println("Even numbers:");
      eval(list, n-> n%2 == 0 );

      System.out.println("Numbers that greater than  5:");
      eval(list, n -> n > 5 );
   }

   public static void eval(List<Integer> list, Predicate<Integer> predicate) {
      for(Integer n: list) {

         if(predicate.test(n)) {
            System.out.println(n);
         }
      }
   }
}

默认方法

Java 8在接口方面引入了一个关于默认方法实现的新概念。它也是作为一种向后兼容能力而出现,旧的接口也能用到Lambda表达式中。例如,ListCollection 接口是没有forEach方法的声明的。但是,通过这些默认方法能够就能轻易地打破集合框架实现的限制。Java 8引入默认方式使得 ListCollection 接口能够拥有 forEach 方法的默认实现。实现了这些接口的类也不必再实现相同的功能了。

语法如下所示:

本段代码是示例代码,仅供理解使用。

public interface boy {
    default void print(){
        System.out.println("I am a boy");
    }
}

多个默认值

接口中有了默认方法之后,在同一个类里面实现两个带有相同默认方法的接口就可行了。

下面的代码演示了如何解决这种含糊不清的情况。

首先是同一个类里面的两个接口

本段是示例代码,仅供理解

public interface younger {
    default void print(){
        System.out.println("I am a younger.");
    }
}

public interface learner {
    default void print(){
        System.out.println("I am a learner.");
    }
}

第一个解决办法就是创建一个自有的办法,来重写默认的实现。就像这样:

本段代码是示例代码,仅供理解使用。

public class student implements younger, learner {
   public void print(){
      System.out.println("I am a younger and a learner, so I am  a student.");
   }
}

另一个解决办法就是使用超类 super 来调用特定接口的默认方法。

本段代码是示例代码,仅供理解使用

public class student implements younger, learner {
    public void print(){
        learner.super.print();
    }
}

静态默认方法

你也可以为这个接口增加静态的辅助方法(helper), 就像下面这样:

本段代码是示例代码,仅供理解使用

public interface Younger {
    default void print(){
        System.out.println("I am a younger.");
    }

    static void sayHi(){
        System.out.println("Young is the capital.");
    }
}

一个默认方法的例子

下面我们通过一个例子来掌握如何使用默认方法。请看下面的代码

public class NewFeaturesTester {
    public static void main(String args[]) {
        Younger younger = new Student();
        younger.print();
    }
}

interface Younger {
    default void print() {
        System.out.println("I am a younger.");
    }

    static void sayHi() {
        System.out.println("Young is the capital.");
    }
}

interface Learner {
    default void print() {
        System.out.println("I am a learner.");
    }
}

class Student implements Younger, Learner {
    public void print() {
        Younger.super.print();
        Learner.super.print();
        Younger.sayHi();
        System.out.println("I am a student!");
    }
}

Optional 类

Optional是一个容器对象,用于容纳非空对象。Optional对象通过缺失值值代表null。这个类有许多实用的方法来促使代码能够处理一些像可用或者不可用的值,而不是检查那些空值(null)。Java 8中引入的这个特性有点像Google Guava里的Optional(Guava 是一个 Google 的基于Java 6的类库集合的扩展项目)。

在Java官方文档的解释中,它是一个可以为null的容器对象。如果值存在则 isPresent() 方法会返回 true ,调用 get()方法会返回该对象。

类的声明及方法

下面是java.util.Optional<T>类的声明:

public final class Optional<T>
extends Object

这个类继承了java.lang.Object类大多数方法。主要有:

接口 描述
static <T> Optional<T> empty() 该方法返回一个空的Optional实例
boolean equals(Object obj) 该方法可以指示某个对象是否与当前Optional对象相等
Optional<T> filter(Predicate<? super <T> predicate) 如果一个值存在并且这个值满足某个给定的断言,那么该方法将返回一个描述该值的Optional对象;否则,将返回一个空的Optional对象
Optional flatMap(Function<? super T,Optional> mapper) 如果一个值存在,该方法会把一个map方法应用于它,并且返回结果;否则,将返回空的Optional对象
T get() 如果一个值存在于当前Optional中,则返回这个值;否则将抛出一个NoSuchElementException异常
int hashCode() 返回当前值的hash编码值。若这个值不存在,则返回0
void ifPresent(Consumer<? super T> consumer) 如果一个值存在,该方法会通过该值调用指定的consumer。如果不存在,则不调用
boolean isPresent() 返回一个值是否存在
Optional map(Function<? super T,? extends U> mapper 如果一个值存在,则将某个map方法应用于它。如果这个值是非空的,则返回一个描述结果的Optional对象
static <T> Optional<T> of(T value) 返回某个存在的非空值的Optional对象

更多的你可以访问官方API手册查看:http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

一个Optional类的例子

import java.util.Optional;

public class NewFeaturesTester {
   public static void main(String args[]){

      NewFeaturesTester tester = new NewFeaturesTester();
      Integer value1 = null;
      Integer value2 = new Integer(5);

      // ofNullable 允许传参时给出 null
      Optional<Integer> a = Optional.ofNullable(value1);

      // 如果传递的参数为null,那么 of 将抛出空指针异常(NullPointerException)
      Optional<Integer> b = Optional.of(value2);
      System.out.println(tester.sum(a,b));
   }

   public Integer sum(Optional<Integer> a, Optional<Integer> b){

      // isPresent 用于检查值是否存在

      System.out.println("First parameter is present: " + a.isPresent());
      System.out.println("Second parameter is present: " + b.isPresent());

      // 如果当前返回的是传入的默认值,orElse 将返回它
      Integer value1 = a.orElse(new Integer(0));

      // get 用于获得值,条件是这个值必须存在
      Integer value2 = b.get();
      return value1 + value2;
   }
}

扩展阅读

Java 8 Optional类深度解析

参考链接

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

推荐阅读更多精彩内容