Как избежать предупреждения «тест типа не может быть проверен» при сопоставлении шаблонов типов, допускающих значение NULL?

Целью следующего метода является удаление нулевых значений.

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
    items.collect { case element: T => element }

Код работает во время выполнения, несмотря на предупреждение компилятора: «Проверка типа для T не может быть проверена во время выполнения, поскольку она ссылается на член абстрактного типа или параметр типа».

Добавление ClassTag[T] в качестве параметра контекста устраняет предупреждение, но я думаю, что использование ClassTag в этом случае является излишним.


Вот еще один подход:

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
    items.collect {
        case element if element != null => element.asInstanceOf[T]
    }

В этом случае требуется asInstanceOf[T], иначе компилятор выдаст сообщение. Очевидно, он игнорирует тот факт, что значение null является единственным экземпляром типа Null.

Ограниченным и неэффективным обходным решением было бы определение экстрактора. Он по-прежнему использует instanceOf, но только один раз, а не каждый match в вашем коде.

object NotNull {
    def unapply[T <: AnyRef](scrutinee: T | Null): Option[T] =
        if scrutinee == null then None
        else Some(scrutinee.asInstanceOf[T])
}

Этот экстрактор ограничен, поскольку он решает только те случаи, когда совпадение не является исчерпывающим.

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
    items.collect {
        case NotNull(element) => element
    }

Это не применимо к исчерпывающим совпадениям, подобным приведенным ниже.

(nullable: T <: AnyRef) match { // the compiler complains with "not exhaustive"
    case NotNull(nonNullable) => Some(nonNullable)
    case null => None
}

Компилятор предупреждает, что совпадение не является исчерпывающим. Он недостаточно умен, чтобы заметить, что совпадение на самом деле является исчерпывающим.

Экстрактор также неэффективен, поскольку метод unapply создает экземпляр Some во время выполнения, что я считаю слишком большими накладными расходами с единственной целью подавления ложного предупреждения компилятора.

Что я могу сделать в scala 3.4, чтобы избежать предупреждения о проверке типа без ретрансляции ClassTag или asInstanceOf? Является ли это возможным?

Я думаю, что ответ на Scala 3: docs.scala-lang.org/scala3/reference/other-new-features/…

Mateusz Kubuszok 27.04.2024 20:07

@MateuszKubuszok Спасибо за ссылку. Я прочитал это, но не понимаю, как с помощью TypeTag можно избежать накладных расходов, от которых страдает ClassTag: дополнительный параметр и создание объекта экстрактором. Слишком много накладных расходов во время выполнения только для того, чтобы удалить ложное предупреждение.

Readren 28.04.2024 09:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
113
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете отключить предупреждение «тест типа для T не может быть проверен во время выполнения, поскольку он относится к члену абстрактного типа или параметру типа» с помощью scala.unchecked

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
  items.collect { case element: T @unchecked => element }

Думаю, вас могут заинтересовать настройки Scala 3.

scalacOptions += "-Yexplicit-nulls"

https://docs.scala-lang.org/scala3/reference/experimental/explicit-nulls.html

Явные значения NULL хорошо работают в некоторых сценариях:

type T
val item: T | Null = null

item match
  case null => println("null")
  case _ =>
    item: T // compiles
    println("not null")
// prints: null

if item != null then
  item: T // compiles
  println("not null")
else println("null")
// prints: null

Проблема в том, что в настоящее время этот вид вывода типа (потоковая типизация) работает со стабильным значением val (как указано выше item).

Мы добавили простую форму вывода типа, чувствительного к потоку. Идея состоит в том, что если p — это стабильный путь или отслеживаемая переменная, то мы можем знать, что p не равно нулю, если сравнивать ее с null. Эту информацию затем можно передать в ветви then и else оператора if (помимо других мест).

пока element внутри

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
  items.collect {
    case element if element != null => element // compile error: Found: (element : T | Null) Required: T
  }

не является стабильным.

Вы можете использовать .nn

def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
  items.collect {
    case element if element != null => element.nn
  }
def filterOutNulls[T](items: Iterable[T | Null]): Iterable[T] =
  items.collect(Function.unlift(item => Option(item).map(_.nn)))

Другие вопросы по теме