Options
An option is a wrapper for a value that might be present or not.
sealed abstract class Option[+A]
// The class Some wraps a concrete value
case class Some[+A](x: A) extends Option[+A]
// The class None is a singleton for absent value
case object None extends Option[Nothing]
For example
val myOption: Option[Int] = Some(1)
val noOption: Option[Int] = None
Unsafe API
Option is very useful for unsafe APIs. It does the Null check for us.
If we work with unsafe APIs:
def unsafeMethod(): String = null
val result = Option(unsafeMethod()) // Some or None
Option makes sure we receive either Some
or None
. What's more, we can define a default value with the chained method, orElse
.
def backupMethod(): String = "default"
val result = Option(unsafeMethod()).orElse(backupMethod())
If we design unsafe APIs, we always return Option.
def unsafeMethod(): Option[String] = None
def def backupMethod(): Option[String] = Some("default")
So that, we can better use them:
val result = unsafeMethod() orElse backupMethod()
Functions on Option
val numOption: Option[Int] = Some(1)
-
isEmpty
println(numOption.isEmpty) // false
-
map
println(numOption.map(num => num * 2)) // Some(2)
-
flatMap
println(numOption.flatMap(num => Option(num * 10))) // Some(10)
-
filter
println(numOption.filter(num => num > 10)) // None
-
get
: It gets the value ofOption
. Avoid using it.
Exercise
Create a connection
if host
and port
are given.
val config: Map[String, String] = Map(
"host" -> "127.0.0.1",
"port" -> "8080"
)
class Connection {
def connect() = "Connected"
}
object Connection {
val random = new Random(System.nanoTime())
def apply(host: String, port: String) = {
if (random.nextBoolean()) Some(new Connection)
else None
}
}
Normal call
val host = config.get("host")
val port = config.get("port")
val connection: Option[Connection] = {
if (!host.isEmpty && !port.isEmpty)
Connection(host.get, port.get)
else None
}
val status = connection.map(conn => conn.connect())
status.foreach(println)
Chained call
val status = config
.get("host")
.flatMap(
host => config
.get("port")
.flatMap(port =>
Connection(host, port)))
.map(conn => conn.connect())
status.foreach(println)
For Comprehensions call
val status = for {
host <- config.get("host")
port <- config.get("port")
conn <- Connection(host, port)
} yield conn.connect()
status.foreach(println)