java - 什么时候应该在 Java 8 中使用 Supplier?

标签 java java-8 functional-interface

这段代码有什么区别?

Supplier<LocalDate> s1 = LocalDate::now;
LocalDate s2 = LocalDate.now();

System.out.println(s1.get()); //2016-10-25
System.out.println(s2); //2016-10-25

我开始学习 Java 8 中的函数式接口(interface),但不了解供应商的好处。何时以及如何使用它们。供应商是否提高了性能或抽象级别的好处?

感谢您的回答!这不是重复的问题,因为我使用搜索并没有找到我需要的东西。

更新 1: 你是说这个吗?

    Supplier<Long> s1 = System::currentTimeMillis;
    Long s2 = System.currentTimeMillis();

    System.out.println(s1.get()); //1477411877817
    System.out.println(s2); //1477411877817
    try {
        Thread.sleep(3000l);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(s1.get()); //1477411880817 - different
    System.out.println(s2); //1477411877817

最佳答案

我将介绍一个我们应该使用 Supplier<LocalDate> 的场景而不是 LocalDate .

直接调用静态方法的代码,如 LocalDate.now()很难进行单元测试。考虑一个场景,我们要对方法进行单元测试 getAge()计算一个人的年龄:

class Person {
    final String name;
    private final LocalDate dateOfBirth;

    Person(String name, LocalDate dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now());
    }
}

这在生产中运行良好。但是单元测试要么必须将系统的日期设置为已知值,要么每年更新一次以期望返回的年龄增加一个,这两种解决方案都是非常棒的。

更好的解决方案是让单元测试在已知日期注入(inject),同时仍允许生产代码使用 LocalDate.now() .也许是这样的:

class Person {
    final String name;
    private final LocalDate dateOfBirth;
    private final LocalDate currentDate;

    // Used by regular production code
    Person(String name, LocalDate dateOfBirth) {
        this(name, dateOfBirth, LocalDate.now());
    }

    // Visible for test
    Person(String name, LocalDate dateOfBirth, LocalDate currentDate) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
        this.currentDate = currentDate;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, currentDate);
    }

}

考虑这样一个场景:自对象创建以来,这个人的生日已经过去了。通过此实现,getAge()将基于创建 Person 对象的时间而不是当前日期。我们可以使用 Supplier<LocalDate> 来解决这个问题:

class Person {
    final String name;
    private final LocalDate dateOfBirth;
    private final Supplier<LocalDate> currentDate;

    // Used by regular production code
    Person(String name, LocalDate dateOfBirth) {
        this(name, dateOfBirth, ()-> LocalDate.now());
    }

    // Visible for test
    Person(String name, LocalDate dateOfBirth, Supplier<LocalDate> currentDate) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
        this.currentDate = currentDate;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, currentDate.get());
    }

    public static void main(String... args) throws InterruptedException {
        // current date 2016-02-11
        Person person = new Person("John Doe", LocalDate.parse("2010-02-12"));
        printAge(person);
        TimeUnit.DAYS.sleep(1);
        printAge(person);
    }

    private static void printAge(Person person) {
        System.out.println(person.name + " is " + person.getAge());
    }
}

正确的输出是:

John Doe is 5
John Doe is 6

我们的单元测试可以像这样注入(inject)“现在”日期:

@Test
void testGetAge() {
    Supplier<LocalDate> injectedNow = ()-> LocalDate.parse("2016-12-01");
    Person person = new Person("John Doe", LocalDate.parse("2004-12-01"), injectedNow);
    assertEquals(12, person.getAge());
}

关于java - 什么时候应该在 Java 8 中使用 Supplier?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40244571/

相关文章:

java - collectingAndThen方法足够高效吗?

java - 无法解析 DateSetListener 对象

java - 如何在android中绘制全屏?

java - @Secured 和 @PreAuthorize 在 Controller 中工作正常,但在服务级别中不起作用

java-8 - 如何从流中获取可观察的 float 组?

java - 重新设计方法以使用 Java 8 Map.computeIfAbsent() 并抛出异常

java - 如何从数组中随机选择一个元素

java - 在 Java 中使用 BiFunction 链接函数

java - 是否可以在 Java 8 中使用对象的特定方法(或静态方法)作为仿函数

java - 有没有办法在 24 以下的 Android API 上使用 Java 8 功能接口(interface)?