2012-11-29 18 views
8

So che in Scala un metodo non dovrebbe mai restituire null ... ma cosa si tratta dei parametri di input? Dato il seguente frammento di codice ...Come gestire i parametri di input nulli in Scala

object MyObject { 

    def myMethod(p: String): Option[String] = { 
     if (p == null) throw new IllegalArgumentException("p is null.") 
     ... 
    } 
} 

... è il mio modo di controllare p corretta? C'è qualche raccomandazione?

risposta

19

La convenzione è che il codice Scala non usa valori nulli (con un numero limitato di eccezioni, che dovrebbero essere immediatamente risolti quando si usano quelle funzioni di libreria).

Quindi, un valore di Scala è un segno che qualcosa è andato storto (almeno uno PBCAK), quindi si può anche lanciare un'eccezione. Questa non è un'operazione di routine; questa è una cosa seriamente incasinata. Cattura l'eccezione ovunque si rilevino gravi errori. La cattura di uno IllegalArgumentException invece di uno NullPointerException non aggiunge ulteriori informazioni. Lascia solo l'originale.

Se il codice proviene da Java, il modo canonico di gestirlo è di inserirlo in Option, che convertirà null in None. Quindi probabilmente non hai nemmeno bisogno di lanciare un'eccezione; basta restituire un None.

def myMethod(p: String) = Option(p).map(_.toLowerCase) 

Se non è possibile continuare quando è nullo, è necessario considerare se un'eccezione informativa potrebbe essere d'aiuto. Option(p).orElse(throw new IllegalArgumentException("Null!")) è un modo compatto per esprimere il sentimento di lancio delle eccezioni.

In Scala 2.10, è inoltre possibile eseguire il wrapping delle cose in scala.util.Try(...) che catturerà e impacchetterà automaticamente l'eccezione per te. Se vuoi un'eccezione pacchettizzata invece di una generata, questa è la strada da percorrere.(E utilizzare Try invece di Option.)

import scala.util.Try 
def myMethod(p: String) = Try(p.toLowerCase) 

Infine, per la gestione più generale degli esiti alternativi, usare Either. La convenzione per la gestione degli errori è che l'output previsto è un Right(whatever), mentre Left(whatever) indica che qualcosa è andato storto.

+1

Dato che sto scrivendo un'API, penso il costrutto più appropriato è Try. – j3d

+1

Fare attenzione quando si avvolgono valori null in un'opzione. Se ti affidi all'inferenza di tipo, a volte potresti incontrare dei problemi. Ad esempio, se fai Option (null) .map (_ + 1) otterrai un'esenzione. Tuttavia, fare Option (null: String) .map (_. ToLowerCase) va bene –

6

Ci sono diversi modi e il tuo modo è uno.

Si potrebbe anche usare:

require(p != null, "p is null.") 

o il modo "più funzionale" sarebbe quella di utilizzare Option:

def myMethod(p: String): Option[String] = { 

    // no exception 
    for { 
    pp <- Option(p) 
    } yield p + " foo bar" 
} 

edit:

o se si vuole l'errore senza un eccezione generata puoi usare Either:

def myMethod(p: String): Either[Exception, String] = { 
    if(p == null) Left(new IllegalArgumentException("p is null.")) 
    else Right(p + "foo bar") 
} 
+2

Tuttavia, si pone la domanda; perché non usare un metodo solo in un'opzione [stringa]? –

+0

Questo è il motivo per cui scala è un linguaggio così incasinato. Per me un'opzione nella firma del metodo indica che il parametro è facoltativo. Cosa succede se il parametro non è opzionale e non si desidera che il chiamante passi vuoto o vuoto (la chiamata utilizza Java)? A volte mi chiedo perché le persone semplicemente non si attengono alla semplicità e usano Java/C++. Può essere prolisso, ma è leggibile e in questi giorni è avanzato abbastanza bene da essere utilizzato per la programmazione distribuita. – animageofmine

0

L'idioma Scala è essenzialmente "non utilizzare mai valori null". Quindi, a meno che tu non stia scrivendo un'API che deve ospitare gli utenti di Java sporchi, non me ne preoccuperei. Altrimenti, finirai per inserire questo controllo boilerplate per ogni parametro su ogni metodo che scrivi.

I suoi bellissimi i metodi di una sola riga

def square(x: String): String = math.pow(x.toInt, 2).toInt.toString 

si trasformerà in qualcosa di scomodi come

def square(x: String): String = 
    if (x == null) throw new NullPointerException() 
    math.pow(x.toInt, 2).toInt.toString 
} 

o

def square(x: String): String = Option(x) match { 
    case Some(x) => math.pow(x.toInt, 2).toInt.toString 
    case None => throw new NullPointerException() 
} 

Quanto terribile.

+0

'Double' è primitivo. Non può essere "null". –

+0

Cambiato in un nuovo esempio con Strings :) –

+0

Non posso dire che sia più bello .... –

0

Il mio approccio preferito è qualcosa di simile:

object HandlingInputValidation { 

    def main(args: Array[String]) = { 
    println("A1: " + handleInput("Correct ") + " END") 
    println("A2: " + handleInput("Correct") + " END") 
    println("A3: " + handleInput("") + " END") 
    println("A4: " + handleInput(" ") + " END") 
    println("A5: " + handleInput(null) + " END") 
    } 

    def handleInput(data: String): Option[String] = { 
    Option(data).map(_.trim.toLowerCase) 
    } 
} 

In questo caso, null sarà None, e spazi verrà ritagliata.

Problemi correlati