class ExchangeRatesServiceImpl : ExchangeRatesService {
private var container: ExchangeRatesContainer? = null
/**
* {@inheritDoc}
*/
override val currentRates: Map<Currency, BigDecimal>
get() {
if (container == null || container.date != LocalDate.now()) {
container = client.getRates(Currency.getBase())
log.info("exchange rates has been updated: {}", container)
}
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container.rates[Currency.EUR.name],
Currency.RUB, container.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
}
}
如果(容器==空|| container.date!= LocalDate.now()) 错误【因为“容器”是可变属性,这一次可能已经更改了
我如何编译它?
答案 0 :(得分:2)
container
在函数范围之外声明,因此理论上,在检查null
并使用它之间,另一个线程可以修改它。为防止这种情况,您应该创建一个初始化为container
的局部变量,并像这样使用它:
class ExchangeRatesServiceImpl : ExchangeRatesService {
private var container: ExchangeRatesContainer? = null
/**
* {@inheritDoc}
*/
override val currentRates: Map<Currency, BigDecimal>
get() {
val tempContainer = container
if (tempContainer == null || tempContainer.date != LocalDate.now()) {
tempContainer = client.getRates(Currency.getBase())
log.info("exchange rates has been updated: {}", tempContainer)
}
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container.rates[Currency.EUR.name],
Currency.RUB, container.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
}
}
答案 1 :(得分:1)
正如apemanzilla已经提到的,这是一个线程问题。即使您只有一个线程,Kotlin也会阻止您这样做,因为有可能另一个线程尝试访问它。这基本上是一场比赛;即使您检查它是否不为null,理论上也有可能在一毫秒后的下一次调用时为null。
但是,对此有多种解决方案。我确实想指出apemanzilla代码的一个缺陷:它没有设置外部容器。这意味着tempContainer == null
始终为真。像这样使用temp变量的主要问题是,它总是最终需要两次调用。另外,临时变量必须是变量。否则无法设置。它只会说“无法重新分配val”。
现在,对于实际解决方案:
第一个(我不建议这样做)使用null断言。 仅 ,如果可以保证它没有被其他线程修改,则这样做,否则将导致程序崩溃:
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container!!.rates[Currency.EUR.name],
Currency.RUB, container!!.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
您还必须在此处执行相同操作:
if (container == null || container!!.date != LocalDate.now()) { ... }
尽管使用null安全实际上更好。将null与非null比较可以。就像if(null != "some string")
(这始终是对的,但您明白了):
if (container == null || container?.date != LocalDate.now) { ... }
如果您愿意,也可以将null-safe与elvis运算符配合使用:
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container?.rates[Currency.EUR.name] ?: TODO(),
Currency.RUB, container?.rates[Currency.RUB.name] ?: TODO(),
Currency.USD, BigDecimal.ONE
)
或者,您可以使用let:
container?.let { /* it -> is explicitly declared here */
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, it.rates[Currency.EUR.name], // You might still need the elvis operator on these if it.rates[something] can return null
Currency.RUB, it.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
}
尽管这会使处理if语句稍微困难一些。
但是 ,这要求您添加第二个退货。如果container为null,它将不会执行该代码,这意味着您需要一个最终的return
语句。您可以尝试递归地重新调用代码,直到成功,返回默认值,引发异常,返回null(无论您喜欢什么)。
最后,如apemanzilla所提到的,是临时变量。
这将创建一个局部变量,该变量在本地是不可变的,这意味着您不会遇到可为空的问题。
val currentRates: Map<Currency, BigDecimal>
get() {
var localContainer = container // This needs to be a var; you assign it, then re-assign it. You can't do that with a `val`
if (localContainer == null || localContainer.date != LocalDate.now()) {
localContainer = client.getRates(Currency.getBase())
log.info("exchange rates has been updated: {}", tempContainer)
container = localContainer // This is also necessary to prevent it from always updating.
}
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container.rates[Currency.EUR.name],
Currency.RUB, container.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
}
尽管,老实说,我不理解您为什么选择container.date != LocalDate.now()
。如果时间不匹配,则总是如此。因此,如果我正确理解了您的代码,则可以将其缩短为:
val currentRates: Map<Currency, BigDecimal>
get() {
val localContainer = client.getRates(Currency.getBase())
// container = localContainer // If you use the container somewhere else.
log.info("exchange rates has been updated: {}", tempContainer)
return ImmutableMap.of<Currency, BigDecimal>(
Currency.EUR, container.rates[Currency.EUR.name],
Currency.RUB, container.rates[Currency.RUB.name],
Currency.USD, BigDecimal.ONE
)
}