Метод Mark вызывает, что он всегда возвращает не нулевой результат

Scala у компилятора есть -Xcheck-null, который пытается проверить, есть ли потенциальная разметка нулевого указателя во время выполнения.

Это нормально для меня, но я получаю слишком много ложных срабатываний, то есть предположим, что я определяю logger:

private final val LOGGER: Logger = LoggerFactory.getLogger(classOf[GenericRestImpl])

Метод getLogger никогда не возвращает null. Как передать эти знания компилятору, чтобы он не жаловался?

[WARNING] TestImpl.scala:31: warning: potential null pointer dereference: LOGGER.debug
[WARNING]       LOGGER.debug("Using {} for sort", sortParam)

Когда я создаю новый экземпляр, я могу отметить его с помощью NotNull trait:

return new Foo() with NotNull.

Это нормально, но что делать с объектами, возвращаемыми из других методов? Особенно, если он идет от сторонней библиотеки? Мне не нравится идея отмечать все мои переменные как Необязательные, потому что это добавит слишком много накладных расходов. Кроме того, мне не нравится идея создания неявных преобразований (потому что для этого класса потребуется дополнительный класс, который я хочу отметить как NotNull.

Я также проверил вопрос Поддержка библиотеки для Scala NotNull trait, но это не помогло решить мою проблему.

Ответ 1

Как упоминает Jatin, NotNull является всего лишь маркером или тегом, поэтому вы можете использовать NotNull для обозначения чего-либо. Трюк для этого заключается в том, чтобы заставить лить ваш базовый тип with NotNull.

Итак, вы можете написать что-то вроде этого "notnull".asInstanceOf[String with NotNull]. Это безопасный бросок, если вы уверены, что он никогда не будет нулевым.

В вашем фактическом примере вы можете написать:

private final val LOGGER: Logger with NotNull = 
   LoggerFactory.getLogger(classOf[GenericRestImpl]).asInstanceOf[Logger with NotNull]

Пока нет необходимости создавать новые типы для этого, это немного громоздко, если вам нужно сделать это много, поэтому вы можете использовать некоторые небольшие утилиты для упрощения/уточнения обозначений:

type NeverNull[T] = T with NotNull
def neverNull[A](a: A): NeverNull[A] = a.asInstanceOf[A with NotNull]

NeverNull - это просто псевдоним для любого типа T с тегами NotNull и NeverNull - небольшая оболочка, чтобы пометить любое существующее значение типа A как никогда не пустое.

Затем вы можете использовать его как:

private final val LOGGER: NeverNull[Logger] = neverNull {
       LoggerFactory.getLogger(classOf[GenericRestImpl])
}

Вы даже можете сделать это неявным преобразованием, если вы действительно уверены в том, что вы делаете:

implicit def neverNull[A](a: A): NeverNull[A] = a.asInstanceOf[A with NotNull]

private final val LOGGER: NeverNull[Logger] = LoggerFactory.getLogger(classOf[GenericRestImpl])

Обратите внимание, что NeverNull[Logger] по-прежнему является Logger, поэтому вы можете вызвать любой метод этого класса на нем или передать его функциям, которые принимают в качестве параметра a Logger.

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