Встроенной функции нет, но вы можете комбинировать filterNotNull
и takeIf
, чтобы получить желаемое поведение:
val a: List<Int?> = listOf(1, null, 3)
val aa: List<Int>? = a.filterNotNull().takeIf { it.size == a.size }
-
Редактировать: согласно @ DrawnRacoon's Предполагается, что было бы быстрее использовать просто takeIf:
val aa: List<Int>? = a.takeIf { null !in a } as List<Int>
Это не создает промежуточный список и короткое замыкание, когда он находит ноль. Требуется небезопасное приведение, чтобы заставить компилятор изменить тип.
Извлеченный из функции расширения, он будет выглядеть так:
@Suppress("UNCHECKED_CAST")
fun <T> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? {
return takeIf { null !in this } as? Iterable<T>
}
Кажется, мы вернулись к вашему первоначальному предложению. Поэтому, возможно, ответ должен был быть следующим:
Нет , вы ничего не упускаете.
В стандартной библиотеке нет метода для такого точного поведения.
Чтобы коснуться того, что происходит в вопросе:
Это связано с эрозией дженериков. Во время выполнения нет универсальных шаблонов, поэтому приведение выполняется с List
до List
, что всегда успешно, и возвращает исходный объект.
Компилятор правильно предупредит вас о небезопасном приведении. Игнорирование этого предупреждения может привести к неправильным типам во время выполнения:
val a = listOf(1, null, 3)
val aa = a as? List<Int> // unsafe cast List<Int?> to List<Int>
// aa is now List<Int> but contains a null
Вы приводите List<Int?>
к List<Int>
, но среда выполнения не может дифференцировать эти типы.
Чтобы привести это безопасно, где-то у нас есть проверить содержимое списка. Лучше сделать это явно, чтобы было ясно, что происходит, поэтому в наших примерах дополнительные вызовы функций.