Как уже упоминалось, apemanzilla - это проблема с многопоточностью.Kotlin не позволяет вам сделать это, даже если у вас есть только один поток, потому что есть вероятность, что другой поток попытается получить к нему доступ.Это в основном гонка;даже если вы проверите, не является ли оно нулевым, есть теоретический шанс, что он может быть нулевым через миллисекунду, когда будут сделаны следующие вызовы.
Однако есть несколько решений для этого.И я хочу указать на недостаток кода apemanzilla: он не устанавливает внешний контейнер.Что означает tempContainer == null
всегда верно.Основная проблема с использованием такой временной переменной заключается в том, что она всегда требует двух вызовов.Также временная переменная должна быть переменной.В противном случае вы не можете установить его.Он просто скажет "val не может быть переназначен"
Теперь для фактического решения:
Первый (я не рекомендую это), использует нулевое утверждение. Только делайте это, если вы можете гарантировать, что он не будет изменен другими потоками, или это приведет к сбою вашей программы:
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-safe на самом деле лучше.Нулевое сравнение с ненулевым - это нормально.Как и 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.
Однако , для этого требуется добавить второй возврат.Если контейнер нулевой, он не будет выполнять этот код, а это означает, что вам нужен заключительный оператор return
.Вы можете попытаться рекурсивно повторно вызывать код до тех пор, пока не добьетесь успеха, не вернете значения по умолчанию, не сгенерируете исключение, не вернете ничего, как вам хочется.
И, наконец, как уже упоминалось 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
)
}