当一个人只能调用一个方法时,为什么要使用 `java.util.function.supplier`?
Why would one use a `java.util.function.supplier`, when one can just call a method?
我在 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();
该值将被初始化一次并且在重新部署之前不会更改。也就是说,如果您改为使用供应商:
public static Supplier<String> SYSTEM_PARAMETER_SUPPLIER = StaticUtilities::retrieveSystemParameter;
当您在某处需要该值时,您将调用 SYSTEM_PARAMETER_SUPPLIER.get()
,它将在需要时检索数据库中的参数 - 这样,如果您更改数据库中的参数,则无需重新部署。
如您所见,供应商很懒惰。当您要求他们工作时(通过调用 .get()),他们会完成工作 - 如果您明智地处理它们,这可能会让您获得一些性能提升。有时你会调用一个方法,该方法期望一个变量 X
传入方法 retrieveX
,然后由于某些条件不满足而最终在方法中不需要 X
。在这种情况下,您将失去性能,因为您将执行检索 X
的代码,而检索 X
的供应商只会在调用 .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 数据库,因为您传递的供应商只有在产品为空时才会被调用。
另一个例子是当你的接受供应商的方法不是纯粹的(即它有副作用),并且副作用发生在调用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()
方法。
当然,应尽可能避免副作用,这只是一个玩具示例,但它显示了可以使用供应商的潜在场所。
供应商增加了一层间接寻址。
考虑到 "All problems in computer science can be solved by another level of indirection",可能有一些问题可以通过使用供应商来解决。
但是,请注意推论“...除了间接层过多的问题。”
所以,如果没有问题需要解决,那么 Supplier 就太过分了,你应该坚持直接调用 new.
换句话说:不信任任何不以解释问题开始的 "pattern" 或 "best practice"(你的问题表明,你实际上不信任,所以继续问这种问题) .
您可以在基于地图的工厂中使用供应商class
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()
,您将返回对同一对象的引用,而不是新对象。
我用它来避免不必要地创建额外的状态:
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();
}
有些人会说这本身是不必要的,但后来它就成了一个哲学问题。
如果没有计算机就不会存在的问题,然后它就变成了现实世界的间接问题。
我承认供应商对我来说可能已经上瘾了,但在我看来,他们感觉像是所有编程公理和原则的自然外推和扩展。
我在 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();
该值将被初始化一次并且在重新部署之前不会更改。也就是说,如果您改为使用供应商:
public static Supplier<String> SYSTEM_PARAMETER_SUPPLIER = StaticUtilities::retrieveSystemParameter;
当您在某处需要该值时,您将调用 SYSTEM_PARAMETER_SUPPLIER.get()
,它将在需要时检索数据库中的参数 - 这样,如果您更改数据库中的参数,则无需重新部署。
如您所见,供应商很懒惰。当您要求他们工作时(通过调用 .get()),他们会完成工作 - 如果您明智地处理它们,这可能会让您获得一些性能提升。有时你会调用一个方法,该方法期望一个变量 X
传入方法 retrieveX
,然后由于某些条件不满足而最终在方法中不需要 X
。在这种情况下,您将失去性能,因为您将执行检索 X
的代码,而检索 X
的供应商只会在调用 .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 数据库,因为您传递的供应商只有在产品为空时才会被调用。
另一个例子是当你的接受供应商的方法不是纯粹的(即它有副作用),并且副作用发生在调用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()
方法。
当然,应尽可能避免副作用,这只是一个玩具示例,但它显示了可以使用供应商的潜在场所。
供应商增加了一层间接寻址。
考虑到 "All problems in computer science can be solved by another level of indirection",可能有一些问题可以通过使用供应商来解决。
但是,请注意推论“...除了间接层过多的问题。”
所以,如果没有问题需要解决,那么 Supplier 就太过分了,你应该坚持直接调用 new.
换句话说:不信任任何不以解释问题开始的 "pattern" 或 "best practice"(你的问题表明,你实际上不信任,所以继续问这种问题) .
您可以在基于地图的工厂中使用供应商class
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()
,您将返回对同一对象的引用,而不是新对象。
我用它来避免不必要地创建额外的状态:
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();
}
有些人会说这本身是不必要的,但后来它就成了一个哲学问题。
如果没有计算机就不会存在的问题,然后它就变成了现实世界的间接问题。
我承认供应商对我来说可能已经上瘾了,但在我看来,他们感觉像是所有编程公理和原则的自然外推和扩展。