当人们可以调用方法时,为什么会使用`java.util.function.supplier`?

时间:2017-01-13 00:20:43

标签: java lambda java-8 functional-interface

我在https://dzone.com/articles/supplier-interface看到了一些使用供应商界面的例子。

我的问题是,如果在上面的例子中我可以做一些简单的事情:

driveVehicle(new Vehicle());
driveVehicle(new Car());

为什么要使用供应商界面,如果只是调用方法,而不接受任何参数。

6 个答案:

答案 0 :(得分:8)

假设您在数据库中存储了要在整个应用中保持不变的参数

// Assume retrieveSystemParameter query database which allows to change parameters
public static String SYSTEM_PARAMETER = StaticUtilities.retrieveSystemParameter();

该值将初始化一次并且不会更改,直到重新部署。话虽如此,如果您使用供应商:

public static Supplier<String> SYSTEM_PARAMETER_SUPPLIER = StaticUtilities::retrieveSystemParameter;

当您需要某个值时,您将调用SYSTEM_PARAMETER_SUPPLIER.get(),它将在需要时检索数据库中的参数 - 这样,如果更改数据库中的参数,则无法重新部署。

如您所见,供应商很懒惰。当你要求他们工作时(通过调用.get()),他们会完成工作 - 如果你明智地处理它们,这可能会让你获得一些性能提升。有时您会调用一个方法,该方法期望变量X传递方法retrieveX,然后在方法中不需要X,因为某些条件未得到满足。在这种情况下,您将失去性能,因为您将执行代码来检索X,而检索X的供应商只会在调用.get时执行它,而您只会执行该调用条件得到满足。

免责声明:系统参数常量只是我想到的第一个例子,但考虑到它在每个.get()上查询数据库,你宁可缓存参数并调用缓存.get()在特定的时间间隔。

答案 1 :(得分:5)

我认为Optional可能是完美的例子。请考虑以下代码段:

final Product firstProduct = Optional.ofNullable(product)
        .orElse(productDao.findProductById(id));

final Product secondProduct = Optional.ofNullable(product)
        .orElseGet(() -> productDao.findProductById(id));

您的产品可能为空。为了确定firstProduct,java必须在orElse方法中调用表达式,因此无论产品是否为null,您都必须确定在产品为空的情况下将返回的值。

为了确定secondProduct数据库,如果产品不为null,则不必查询,因为您传递的是仅当product为null时才会调用的供应商。

答案 2 :(得分:1)

另一个例子是当您接受供应商的方法不纯(即,它有副作用),并且在调用lambda之前发生副作用,并且lambda的行为受副作用影响。

例如,考虑这个例子:

public class TestClass {

    private String field;

    public String getField() {
        return field;
    }

    public void method(Supplier<String> supplier) {
        field = "This is";
        System.out.println(supplier.get() + " a test");
    }

    public static void main(String[] args) {
        TestClass c = new TestClass();
        c.method(() -> c.getField()); 
    }
}

这里,method()不是纯粹的,因为它改变了field的值,后者在lambda中使用(通过调用getField()方法)。当lambda被调用到位时(即,当调用get()时),在设置字段后调用getField()将发生。换句话说,method()接受Supplier<String>而不是String,试图让客户安全地调用getField()方法。

当然,应尽可能避免副作用,这只是一个玩具示例,但它显示了供应商可以使用的潜在位置。

答案 3 :(得分:1)

供应商增加了一个间接层次。

鉴于"All problems in computer science can be solved by another level of indirection",可能存在一些可以通过使用供应商解决的问题。

然而,请注意结果&#34; ......除了间接层太多的问题。&#34;

所以,如果没有问题可以解决,那么供应商就会过度,你应该坚持直接调用 new

换句话说:不信任任何&#​​34;模式&#34;或者&#34;最佳实践&#34;这并不是从解释问题开始的(你的问题表明,你实际上是不信任,所以继续问这类问题)。

答案 4 :(得分:0)

我用它来避免不必要地创建其他状态:

private Supplier<Boolean> detach = () -> false;
private Supplier<Boolean> isAttached = () -> false;
private Supplier<Integer> index = () -> null;

private final Function<List<ObserverWrapper<X, Y>>, Boolean> attachFun = observers -> {
    isAttached = () -> observers.contains(this);
    detach = () -> observers.remove(this);
    index = () -> observers.indexOf(this);
    return observers.add(this);
};

public boolean attach(List<ObserverWrapper<X, Y>> observers) {
    return attachFun.apply(observers);
}

public boolean isAttached() {
    return isAttached.get();
}

public Integer observerIndex() {
    return index.get();
}

有些人会说这本身是不必要的,但随后就变成了一个哲学问题。

如果计算机不存在,该问题将不存在,然后成为现实世界中的间接问题。

我可能承认供应商对我来说可能已经成瘾了,但在我看来,他们感觉就像是所有编程公理和原则的自然推论和扩展。

答案 5 :(得分:-1)

您可以在基于地图的工厂类

中使用供应商
public class StackService {
    final static String INTEGERS = "Integers";
    final static String DOUBLES = "Doubles";
    final static String STRINGS = "Strings";

    final static Map<String, Supplier<Stack>> stackType;

    static {
        stackType = new HashMap<>();
        stackType.put(INTEGERS, Stack<Integer>::new);
        stackType.put(DOUBLES, Stack<Double>::new);
        stackType.put(STRINGS, Stack<String>::new);
    }

    public Stack<?> createStackOfType(String stackType) {
        return stackType.get(stackType).get();
    }
}

如果您只是使用new Stack(),那么您将返回对同一对象的引用而不是新引用。