Пометить вызов метода, чтобы он всегда возвращал ненулевой результат

У компилятора 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:

return new Foo() with NotNull.

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

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


person uthark    schedule 28.08.2013    source источник
comment
Вы правильно понимаете, трейт NotNull — это просто маркер. Больше ничего не делает.   -  person Jatin    schedule 29.08.2013


Ответы (1)


Как упоминает Джатин, 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, поэтому вы можете вызывать для него любой метод этого класса или передавать его функциям, принимающим в качестве параметра Logger.

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

person Mortimer    schedule 04.09.2013