2013-08-28 11 views
6

Il compilatore Scala ha -Xcheck-null che tenta di verificare se ci sono potenziali dereferenze del puntatore nullo in runtime.Chiamata al metodo Mark che restituisce sempre risultato non nullo

E 'ok per me, ma ho troppo falsi positivi, vale a dire supponiamo che io definisco logger:

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

Il metodo getLogger mai restituisce null. Come posso passare questa conoscenza al compilatore in modo che non si possa lamentare?

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

Quando creo nuova istanza posso segnare con NotNull tratto:

return new Foo() with NotNull. 

Questo va bene, ma che cosa fare con gli oggetti restituiti da altri metodi? Soprattutto se proviene da una libreria di terze parti? Non mi piace l'idea di contrassegnare tutte le mie variabili come Facoltativo, perché aggiungerà troppi sovraccarichi. Inoltre, non mi piace l'idea di creare conversioni implicite (perché richiede una classe extra per ogni classe che voglio contrassegnare come NotNull.

Ho anche controllato la domanda Library support for Scala's NotNull trait ma non ha aiutato a risolvere il mio problema.

+0

Capisci bene, il tratto NotNull è solo un indicatore. Non fa niente di più. – Jatin

risposta

5

come cita Jatin, NotNull è solo un indicatore o un tag, in modo da poter utilizzare NotNull per etichettare qualsiasi cosa. Il trucco per farlo è quello di forzare un cast vostro tipo di base with NotNull.

così si può scrivere qualcosa di simile questo "notnull".asInstanceOf[String with NotNull]. È un cast sicuro se si è sicuri che non sia mai stato nullo.

Nel tuo esempio attuale, si può quindi scrivere:

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

Mentre non v'è alcuna necessità di creare nuovi tipi di questo, è un po 'ingombrante se si deve fare un sacco, così si potrebbe usare un po' piccoli utils per semplificare/chiarire la notazione:

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

NeverNull è solo un alias per qualsiasi tipo T contrassegnate con NotNull e neverNull è un piccolo involucro per codificare qualsiasi valore esistente di tipo A come mai nullo.

È possibile quindi utilizzarlo come:

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

Si potrebbe anche fare questo una conversione implicita, se siete davvero sicuri di quello che state facendo:

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

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

noti che NeverNull[Logger] è ancora a Logger, quindi è possibile chiamare qualsiasi metodo di quella classe o passarlo a funzioni che assumono come parametro a Logger.

Questo tipo di costrutto è chiamato un unboxed tipo di tag ed è molto utile, vedere altre applicazioni e discussione here e here.