这个问题类似于Reading multiple variables from an object wrapped in Option[],但在Java而不是Scala的上下文中。
假设我有一个返回Optional<Address>
的方法。
我需要从包装的地址中提取多个字段,例如getCity()
,getCountry()
和getZIP()
。如果该地址不存在,则应使用一些默认值。然后,逻辑应继续使用结果值。
问题是什么是最好的,最惯用的方法?我可以想到以下三个:
1)if-else
City city;
Country country;
ZIP zip;
Optional<Address> optAddr = getAddress();
if (optAddr.isPresent()) {
Address addr = optAddr.get();
city = addr.getCity();
country = addr.getCountry();
zip = addr.getZIP();
}
else {
city = null;
country = Country.getUSA();
zip = ZIP.DEFAULT;
}
// ... use city, country, and zip
优点:
缺点:
2)多个链
Optional<Address> optAddr = getAddress();
City city = optAddr.map(Address::getCity()
.orElse(null);
Country country = optAddr.map(Address::getCountry)
.orElseGet(Country::getUSA);
ZIP zip = optAddr.map(Address::getZIP)
.orElse(ZIP.DEFAULT);
// ... use city, country, and zip
请注意,此处的逻辑略有不同,因为默认值不仅适用于缺少地址,而且适用于对应的地址字段为null
的情况。
但这不是我的问题。
优点:
缺点:
map
的链式调用可能会创建中间对象(但以惯用的方式)。Optional
上单项使用多个链条?3a)在对象中包装默认值
private Address createDefaultAddress() {
Address addr = new Address();
addr.setCity(null);
addr.setCountry(Country.getUSA());
addr.setZIP(ZIP.DEFAULT);
return addr;
}
Address addr = getAddress().orElseGet(this::createDefaultAddress);
City city = addr.getCity();
Country country = addr.getCountry();
ZIP zip = addr.getZIP();
// ... use city, country, and zip
优点:
缺点:
Address
中,以便之后立即提取。3b)将默认值换成常数
如@HariMenon和@flakes所建议的那样,具有默认值的Address
可以存储在常量中,并可以在每次调用中重复使用,以减少开销。
一个人也可以懒惰地初始化“常量”:
private Address defaultAddress;
private Address getDefaultAddress() {
if (defaultAddress == null) {
defaultAddress = new Address();
defaultAddress.setCity(null);
defaultAddress.setCountry(Country.getUSA());
defaultAddress.setZIP(ZIP.DEFAULT);
}
return defaultAddress;
}
Address addr = getAddress().orElseGet(this::getDefaultAddress);
// ... same as 3.1
奖励问题:
假设我完全控制getAddress()
方法,并且我知道所有用法都与此示例相似,但是默认值有所不同。
我应该更改它以返回null
而不是Optional
来更好地适应if-else
解决方案吗?
答案 0 :(得分:1)
这有点主观,所以不会有正确的答案。但是,这是我的2美分,为什么我这么认为。选项1绝对不是Java 8惯用的方式。
在2和3之间,您选择什么取决于您是要使用默认地址,还是要使用单独的默认城市,国家和邮政编码。如果“地址”不为空,但城市为空,您是否仍要使用默认城市?选项3会让事情变得很丑陋。否则,我会选择选项3。即使将其包装到一个对象中,该对象也可以是名为DEFAULT_ADDRESS的常量,然后可以很清楚地知道它是什么,因此可读性更高。
选项2使其看起来好像您有一个单独的默认国家/地区,城市和邮政编码,但实际情况并非如此。但是就像我说的,如果是这样,您应该使用它。
编辑3b和奖励: 如果您正在考虑包装的性能影响,那么您会认为它太过思考了。在Java中创建这样的包装器非常便宜,现代的JIT编译器在大多数情况下都能很好地优化此包装器。性能上的可读性,除非经过测量证明该代码是性能瓶颈。对于Optional vs null,只需选择一种编码样式并坚持使用该项目即可。
如果返回的是Optional,请确保该方法从不返回null(可同时返回Optional和null的方法是可憎的)。许多人认为您应该始终使用Optional
。就个人而言,我略有不同意。如果这是一个新的代码库,并且我们在各处都使用Optionals,那么它很棒。坚持使用它,不要在任何地方使用null和null检查。如果它是旧的代码库,并且已经在多个地方使用了null,则只需使用null,并记录该方法在某些情况下可以返回null。结合使用Optional和null会造成可怕的混乱,我认为Optional并不能提供足够的好处来抵消这一成本。从一开始就支持可选项的语言就很棒。但是它们是Java中的事后添加的,而Java仍然具有null,因此IMO的好处实际上并不是那么大。不过,这又是非常主观的,我将使用的内容会因情况而异。
答案 1 :(得分:0)
我同意@HariMenon的观点。如果我是你,我会选择选项三。但是,与其每次都创建一个新的Address
,不如创建一次值并将地址存储在私有字段中。如果不存在合适的ctor,则可以使用双花括号初始化来稍微简化构造。只需确保不要更改默认的Address
变量即可。
private static final Address DEFAULT_ADDRESS = new Address() {{
setCity(null);
setCountry(Country.getUSA());
setZIP(ZIP.DEFAULT);
}};
...
Address addr = getAddress().orElse(DEFAULT_ADDRESS);