我在https://dzone.com/articles/supplier-interface上看到了一些使用供应商接口的示例。
我的问题是,如果在上面的示例中,我能够做出像下面这样简单的事情:
driveVehicle(new Vehicle());
driveVehicle(new Car());
如果供应商接口只是调用一个方法而不需要输入任何参数,为什么有人想要使用它呢?
我在https://dzone.com/articles/supplier-interface上看到了一些使用供应商接口的示例。
我的问题是,如果在上面的示例中,我能够做出像下面这样简单的事情:
driveVehicle(new Vehicle());
driveVehicle(new Car());
如果供应商接口只是调用一个方法而不需要输入任何参数,为什么有人想要使用它呢?
假设您在数据库中存储了参数,并希望在整个应用程序中保持这些参数不变。
// 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()。
我想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为空时才会被调用。
Supplier增加了一个间接层级。
鉴于"计算机科学中的所有问题都可以通过增加一个间接层级来解决", 使用Supplier可能会解决一些问题。
但要注意,有一个推论“...除了过多的间接层级问题。”
因此,如果没有需要解决的问题,那么使用Supplier就是杀鸡焉用牛刀,应该直接调用new。
换句话说:不要相信任何不从解释问题开始的“模式”或“最佳实践”(您的问题表明您确实不信任,所以继续问这类问题即可)。
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()
方法。我使用它来避免不必要的额外状态创建:
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();
}
有人会说这本身是不必要的,但它随之成为了一个哲学问题。
如果没有计算机,这个问题将不存在,然后它将成为一种间接引用的现实世界问题。
我承认,对于我来说,供应商可能已经成为了一种瘾,但在我的脑海中,它们感觉像是所有编程公理和原则的自然推演和延伸。
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()
,你会返回一个指向同一对象的引用,而不是一个新对象。
new VehiclePainter(vehicleSupplier)
--这个画家并不关心车辆来自哪里,或者它们是汽车、卡车还是飞机。它只接受供应商提供的车辆,并对它们进行涂漆。 - yshavitSupplier<Vehicle>
而不是Supplier<Car>
),但也仅此而已。 - yshavit