为什么要使用 `java.util.function.supplier`,而不是直接调用方法?

3

我在https://dzone.com/articles/supplier-interface上看到了一些使用供应商接口的示例。

我的问题是,如果在上面的示例中,我能够做出像下面这样简单的事情:

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

如果供应商接口只是调用一个方法而不需要输入任何参数,为什么有人想要使用它呢?


2
如果您不想硬编码数值,该怎么办? - chrylis -cautiouslyoptimistic-
2
如果你想要一个能够动态生成新车辆的东西,但是你不希望消费它们的代码处理创建它们的逻辑怎么办?new VehiclePainter(vehicleSupplier)--这个画家并不关心车辆来自哪里,或者它们是汽车、卡车还是飞机。它只接受供应商提供的车辆,并对它们进行涂漆。 - yshavit
@yshavit 你能否请用一些代码详细说明一下? - AmeyaKetkar
2
这基本上就是Jean-François Savard在他们的回答中提到的。我加了一个小变化,即所提供的东西是多态的(Supplier<Vehicle>而不是Supplier<Car>),但也仅此而已。 - yshavit
6个回答

11

假设您在数据库中存储了参数,并希望在整个应用程序中保持这些参数不变。

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

只要不重新部署,该值将被初始化一次并且不会更改。话虽如此,如果您改用 supplier:

public static Supplier<String> SYSTEM_PARAMETER_SUPPLIER = StaticUtilities::retrieveSystemParameter;
当您需要在某处使用一个值时,您将调用 SYSTEM_PARAMETER_SUPPLIER.get(),它将在需要时从数据库中检索参数 - 这样,如果您更改了数据库中的参数,则无需重新部署。

正如您所看到的,Suppliers 是惰性的。当您要求它们工作时(通过调用 .get()),它们才会开始工作 - 如果您明智地处理它们,可能可以获得一些性能提升。有时您会调用一个方法,该方法期望在方法 retrieveX 中传递变量 X,然后最终不需要在方法中使用 X,因为未满足某些条件。在这种情况下,您将失去性能,因为您将执行检索 X 的代码,而检索 X 的 supplier 只会在调用 .get 时执行它,并且只有在满足条件时才会进行该调用。


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


11

我想Optional可能是一个完美的例子。考虑下面的代码片段:

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

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

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

为了确定secondProduct,如果product不为空,则无需查询数据库,因为您正在传递一个Supplier,该Supplier仅在product为空时才会被调用。


1

Supplier增加了一个间接层级。

鉴于"计算机科学中的所有问题都可以通过增加一个间接层级来解决", 使用Supplier可能会解决一些问题。

但要注意,有一个推论“...除了过多的间接层级问题。”

因此,如果没有需要解决的问题,那么使用Supplier就是杀鸡焉用牛刀,应该直接调用new

换句话说:不要相信任何不从解释问题开始的“模式”或“最佳实践”(您的问题表明您确实不信任,所以继续问这类问题即可)。


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()方法。
当然,在任何可能的情况下都应该避免副作用,这只是一个玩具示例,但它展示了供应商可以使用的潜在场所。

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

有人会说这本身是不必要的,但它随之成为了一个哲学问题。

如果没有计算机,这个问题将不存在,然后它将成为一种间接引用的现实世界问题。

我承认,对于我来说,供应商可能已经成为了一种瘾,但在我的脑海中,它们感觉像是所有编程公理和原则的自然推演和延伸。


-1
你可以在基于映射的工厂类中使用Supplier。
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(),你会返回一个指向同一对象的引用,而不是一个新对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接