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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40244571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:02:59  来源:igfitidea点击:

When we should use Supplier in Java 8?

javajava-8functional-interface

提问by badCoder

What difference between this code?

这段代码有什么区别?

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

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

I start learning functional interfaces in Java 8 and don't understand the benefit of the Supplier. When and how, exactly, should use them. Does the Supplier improve performance or maybe the benefits on abstraction level?

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

Thanks for your answers! And it isn't duplicate question because I used search and didn't find what I need.

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

UPDATE 1:You mean this?

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

采纳答案by Bálint

It definitely doesn't improve the performance. Your question is similar to this one: Why are we using variables? We could simply just recalculate everything every time we need it. Right?

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

If you need to use a method a lot of times, but it has a wordy syntax.

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

Let's assume you have a class named MyAmazingClass, and you have a method in it with the name MyEvenBetterMethod(which is static), and you need to call it 15 times at 15 different positions in your code. Of course, you can do something like...

假设您有一个名为 的类MyAmazingClass,并且其中有一个具有该名称的方法MyEvenBetterMethod(它是静态的),您需要在代码中的 15 个不同位置调用它 15 次。当然,你可以做一些像......

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

...but you can also do

...但你也可以这样做

Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod;

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

回答by joshden

I'll go through a scenario where we should use Supplier<LocalDate>instead of LocalDate.

我将经历一个我们应该使用Supplier<LocalDate>而不是LocalDate.

Code that directly makes calls to static methods like LocalDate.now()is very difficult to unit test. Consider a scenario where we want to unit test a method getAge()that calculates a person's age:

直接调用静态方法的代码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());
    }
}

This works fine in production. But a unit test would either have to set the system's date to a known value or be updated every year to expect the returned age to be incremented by one, both pretty aweful solutions.

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

A better solution would be for the unit test to inject in a known date while still allowing the production code to use LocalDate.now(). Maybe something like this:

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

}

Consider a scenario where the person's birthday has passed since the object was created. With this implementation, getAge()will be based on when the Person object was created rather than the current date. We can solve this by using Supplier<LocalDate>:

考虑这样一个场景,自创建对象以来,该人的生日已经过去。使用此实现,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());
    }
}

The output would correctly be:

输出正确的是:

John Doe is 5
John Doe is 6

Our unit test can inject the "now" date like this:

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

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

回答by Holger

You are confusing functional interfaces and method references. Supplieris just an interface, similar to Callable, which you should know since Java?5, the only difference being that Callable.callis allowed to throw checked Exceptions, unlike Supplier.get. So these interfaces will have similar use cases.

您混淆了函数式接口和方法引用。Supplier只是一个接口,类似于Callable,你应该知道从 Java?5 开始,唯一的区别Callable.call是允许抛出检查的Exceptions,不像Supplier.get. 所以这些接口会有类似的用例。

Now, these interface also happen to be functional interfaces, which implies that they can be implemented as a method reference, pointing to an existing method that will be invoked when the interface method is invoked.

现在,这些接口也恰好是函数式接口,这意味着它们可以作为方法引用来实现,指向一个现有的方法,当调用接口方法时将调用该方法。

So before Java?8, you had to write

所以在 Java?8 之前,你必须写

Future<Double> f=executorService.submit(new Callable<Double>() {
    public Double call() throws Exception {
        return calculatePI();
    }
});
/* do some other work */
Double result=f.get();

and now, you can write

现在,你可以写

Future<Double> f=executorService.submit(() -> calculatePI());
/* do some other work */
Double result=f.get();

or

或者

Future<Double> f=executorService.submit(MyClass::calculatePI);
/* do some other work */
Double result=f.get();

The question when to useCallablehasn't changed at all.

何时使用的问题Callable根本没有改变。

Similarly, the question when to use Supplieris not dependent on how you implement it, but which API you use, i.e.

同样,何时使用的问题Supplier不取决于您如何实现它,而是您使用哪个 API,即

CompletableFuture<Double> f=CompletableFuture.supplyAsync(MyClass::calculatePI);
/* do some other work */
Double result=f.join();// unlike Future.get, no checked exception to handle...

回答by Ed Bighands

I am sure your query has been answered by the answers provided already. This is my understanding of Supplier.

我相信您的查询已经被提供的答案回答了。这是我对供应商的理解。

It give me the default Java defined interfaces that acts as a wrapper for any lambda method ref target return time. Simply put, anywhere you have written a method that has no args and returns an Object, qualifies to be wrapped by the Supplier Interface. Without that, in pre generics, you would capture the return as object class and then cast and in generics you would define your own T(ype) to hold the return value. It just provides a uniform return type generic holder on which you can invoke get() to extract the returned object from the no arg method mentioned above.

它为我提供了默认的 Java 定义的接口,作为任何 lambda 方法引用目标返回时间的包装器。简而言之,只要你编写了一个没有参数并返回一个对象的方法,就有资格被供应商接口包装。没有它,在预泛型中,您将返回作为对象类捕获,然后进行转换,在泛型中您将定义自己的 T(ype) 来保存返回值。它只是提供了一个统一的返回类型泛型持有者,您可以在该持有者上调用 get() 从上面提到的 no arg 方法中提取返回的对象。

回答by Htnamus

The explanation in Item 5 of Effective Java by Joshua Blochcleared this up for me:

Joshua BlochEffective Java第 5 条中的解释为我澄清了这一点:

The Supplier interface, introduced in Java 8, is perfect for representing factories. Methods that take a Supplier on input should typically constrain the factory's type parameter using a bounded wildcard type (Item 31) to allow the client to pass in a factory that creates any subtype of a specified type. For example, here is a method that makes a mosaic using a client-provided factory to produce each tile:

在 Java 8 中引入的供应商接口非常适合表示工厂。在输入上使用供应商的方法通常应该使用有界通配符类型(条目 31)来约束工厂的类型参数,以允许客户端传入创建指定类型的任何子类型的工厂。例如,下面是一种使用客户提供的工厂生产每个瓷砖来制作马赛克的方法:

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

You might want to send in a resource factory to an object which will make objects using the resource factory. Now, let's say you want to send in different resource factories with each resource factory generating objects in different positions of the inheritance tree and yet the object receiving the resource factory must be able to receive them in any case.

您可能希望将资源工厂发送到将使用资源工厂创建对象的对象。现在,假设您要发送不同的资源工厂,每个资源工厂在继承树的不同位置生成对象,但接收资源工厂的对象无论如何必须能够接收它们。

Then you can implement a supplier which can accept methods that return objects extending/implementing a certain class/interface using the bounded wildcard and use the supplier as a resource factory.

然后,您可以实现一个供应商,该供应商可以接受返回使用有界通配符扩展/实现某个类/接口的对象的方法,并将供应商用作资源工厂。

Reading Item 5 of the book might help in completely understanding the usage.

阅读本书的第 5 项可能有助于完全理解用法。

回答by Vivek MVK

I will add my view as I am not satisfied by the answers: Supplier is useful when you want to delay the execution.

我将添加我的观点,因为我对答案不满意:当您想延迟执行时,供应商很有用。

Without supplier

无供应商

config.getLocalValue(getFromInternet() /*value if absent*/);

Before getLocalValue gets called getFromInternet will be called, but the value of getFromInternet() will be used only if local value is absent.

在调用 getLocalValue 之前,将调用 getFromInternet,但仅当本地值不存在时才会使用 getFromInternet() 的值。

Now, if config.getLocalValuecan accept supplier, we can delay this execution, plus we won't execute if local value is present.

现在,如果config.getLocalValue可以接受供应商,我们可以延迟此执行,而且如果存在本地值,我们将不会执行。

config.getLocalValue(() -> getFromInternet())

DifferenceSupplier makes it possible: execute only when and if needed

差异供应商使之成为可能:execute only when and if needed

回答by newday

This example of how Suppliercan be used to improve the performance but Supplieritself will not improve the performance.

这个例子说明了如何Supplier可以用来提高性能但Supplier它本身并不会提高性能。

/**
 * Checks that the specified object reference is not {@code null} and
 * throws a customized {@link NullPointerException} if it is.
 *
 * <p>Unlike the method {@link #requireNonNull(Object, String)},
 * this method allows creation of the message to be deferred until
 * after the null check is made. While this may confer a
 * performance advantage in the non-null case, when deciding to
 * call this method care should be taken that the costs of
 * creating the message supplier are less than the cost of just
 * creating the string message directly.
 *
 * @param obj     the object reference to check for nullity
 * @param messageSupplier supplier of the detail message to be
 * used in the event that a {@code NullPointerException} is thrown
 * @param <T> the type of the reference
 * @return {@code obj} if not {@code null}
 * @throws NullPointerException if {@code obj} is {@code null}
 * @since 1.8
 */
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
    if (obj == null)
        throw new NullPointerException(messageSupplier == null ?
                                       null : messageSupplier.get());
    return obj;
}

In this method, the Supplier use in a way that only the object is really null then gets the String. Therefore, it can improve the performance of the operation but it is not Supplierbut how it has been used.

在这种方法中,供应商以一种只有对象真正为空的方式使用,然后获取字符串。因此,它可以提高操作的性能,但不Supplier只是如何使用它。