我们什么时候应该在Java 8中使用供应商?

2022-08-31 20:33:24

此代码之间有什么区别?

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中的功能接口,并不了解供应商的好处。究竟何时以及如何使用它们。供应商是否提高了性能,还是在抽象级别上提高了优势?

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

更新 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

答案 1

我将介绍一个场景,我们应该使用而不是 .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());
    }
}

这在生产中工作正常。但是,单元测试要么必须将系统的日期设置为已知值,要么每年更新一次,以期望返回的年龄增加一个,这两者都是非常令人敬畏的解决方案。

更好的解决方案是让单元测试注入已知日期,同时仍然允许生产代码使用 。也许是这样的: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);
    }

}

考虑一个场景,其中此人的生日自对象创建以来已过。使用此实现,将基于 Person 对象的创建时间,而不是当前日期。我们可以通过使用:getAge()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

我们的单元测试可以像这样注入“now”日期:

@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());
}

答案 2

它绝对不会提高性能。你的问题与此类似:我们为什么要使用变量?每次需要时,我们都可以简单地重新计算所有内容。右?

如果你需要使用一种方法很多次,但它有一个冗长的语法。

假设您有一个名为 的类,并且您有一个名称为的方法(它是静态的),并且您需要在代码中的15个不同位置调用它15次。当然,你可以做这样的事情...MyAmazingClassMyEvenBetterMethod

int myVar = MyAmazingClass.MyEvenBetterMethod();
// ...
int myOtherVar = MyAmazingClass.MyEvenBetterMethod();
// And so on...

...但你也可以做

Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod;

int myVar = shorter.get();
// ...
int myOtherVar = shorter.get();
// And so on...

推荐