Jak se v jazyce Scala brání NullPointerException
? V hojné míře se používá třída Option
, která zajistí, že nemusí metoda vracet null
, ale vrací existující instanci. Jak to celé funguje si ukážeme v Javě:
public abstract class Option<T> {
public abstract T get();
public abstract boolean isNone();
public abstract boolean isSome();
}
Pokud potřebujeme aby nějaká metoda vracela něco a nebo
null
, použijeme třídu Option
. Díky ní metoda vrátí existující instanci, ze které jednoduše zjistíme, zda návratová hodnota má být null
(isNone()
vrací true
) a nebo je to nějaká smyslu plná hodnota (isSome()
vrací true
) a pak se k této hodnotě dostaneme pomocí metody get()
.Teď už to chce jenom nějaký rozumný příklad. Vezněme si např.
Map
a její metodu get
:
V get(T key);
Ta v případě, že klíč v mapě neexistuje vrací null
. Takže typicky píšeme něco jako:
String value = map.get(key);
if (value != null) {
System.out.println(value);
} else {
System.out.println("Nic");
}
A jak by tedy měla metoda get objektu Map vypadat kdybychom byli ve Scale:
Option<V> get(T key);
A nyní jak by vypadal náš kód pro čtení z mapy:
Option<String> value = map.get(key);
if (value.isSome()) {
System.out.println(value.get());
} else {
System.out.println("Nic");
}
A zbavili jsme se ošklivého porovnání na
null
, což jsme chtěli. Navíc pokud metoda vrací Option
a ne přímo hodnotu je podstatně větší pravděpodobnost, že nezapomeneme otestovat zda Option
něco obsahuje. Pokud metoda vrací přímo hodnotu nebo null
, pak hrozí, že hodnotu rovnu použijeme a neotestujeme ji na null
.A nyní přistupme k implementaci:
public class None<T> extends Option<T> {
public T get() {
throw new UnsupportedOperationException("The None had no value");
}
public boolean isNone() {
return true;
}
public boolean isSome() {
return false;
}
public boolean equals(Object obj) {
return ((obj == this) || (obj instanceof None));
}
public int hashCode() {
return 1;
}
}
public class Some<T> extends Option<T> {
protected T value;
public Some(T value) {
super();
this.value = value;
}
public T get() {
return value;
}
public boolean isNone() {
return false;
}
public boolean isSome() {
return true;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof Some) {
Some other = (Some) obj;
return value.equals(other.value);
}
return false;
}
public int hashCode() {
return value.hashCode();
}
}
A jak to celé funguje ve Scale? Díky pattern matchingu a type inference ještě elegantněji:
val value = map.get(key)
value match {
case None => print "Nic"
case Some(x) => print x
}
11 komentářů:
Nevypadly ti tam <T> resp. nezměnily se v neznámé html značky?
Máš pravdu, opraveno ...
Okomentovat