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
}
