Scala: Null, null, Nil, Nothing, None and Unit
Scala включает в себя несколько понятий для обозначения отсутствия значения. Каждое из них используется в строго определенных ситуациях, потому хорошо бы знать, что же каждое их этих понятий означает и где применяется.
Null and null
Null
это трейт, у которого существует единственный экземпляр – null
.
Null
является потомком всех ссылочных классов (AnyRef
), но не примитивных (AnyVal
).
null служит той же цели, что в Java, то есть представляет собой значение ссылочного типа, которое не указывает ни на какой объект. Ссылочные типы могут иметь значение null
, а примитивные (например, Int
) не могут:
scala> val n: Int = null
<console>:11: error: an expression of type Null is
ineligible for implicit conversion
val n: Int = null
Если метод принимает параметр типа Null
, у нас два варианта: передать ему непосредственно null
или ссылку типа Null
.
scala> def f(x: Null): Unit = println("Ok")
f: (x: Null)Unit
scala> f(null)
Ok
scala> val nullRef: Null = null
nullRef: Null = null
scala> f(nullRef)
Ok
При этом, если попробовать передать нулевую ссылку на String
, это не пройдет проверку типов, получим ошибку компиляции:
scala> val stringRef: String = null
stringRef: String = null
scala> f(stringRef)
<console>:14: error: type mismatch;
found : String
required: Null
f(stringRef)
^
Nil
Nil
это объект, который расширяет (extends) List[Nothing]
. То есть это пустой список нулевой длины, которые по сути не означает “пустоту” или “отсутствие значения”, а является объектом, списком, просто без содержимого.
scala> Nil
res1: scala.collection.immutable.Nil.type = List()
scala> Nil.length
res2: Int = 0
scala> "ABC" :: Nil
res3: List[String] = List(ABC)
scala> Nil :: Nil
res4: List[scala.collection.immutable.Nil.type] = List(List())
Nothing
Nothing
это еще один трейт, он расширяет класс Any
(все классы в Scala наследуются от Any
), таким образом Nothing
подтип любого класса в Scala. Важная особенность Nothing
– не существует экземпляров этого класса.
Возвращаясь к Nil
, так как это List[Nothing]
, а Nothing
подтип любого класса, можно использовать Nil
как пустой тип списка String
, Int
или любого другого класса. Это возможно потому, что List – ковариантен. Удобно.
scala> val emptyStringList: List[String] = List[Nothing]()
emptyStringList: List[String] = List()
scala> val emptyIntList: List[Int] = List[Nothing]()
emptyIntList: List[Int] = List()
val emptyIntList: List[Int] = Nil
emptyIntList: List[Int] = List()
Еще одно важное применение Nothing
– в качестве типа возвращаемого значения методов, которые никогда не возвращают результат. Если это обдумать, выходит очень логично: Если тип возвращаемого значения метода Nothing
, и не существует экземпляров типа Nothing
, то выходит, метод никогда не возвращает результат.
Пример таких методов: методы, которые всегда выбрасывают исключения.
None
Бывают ситуации, когда метод не может вернуть полезный результат. Но выбрасывать исключение или возвращать null
– не лучшие решения. Для этого в Scala существует класс Option
. У него ровно два потомка: класс Some[T]
и объект None
.
Цель класса Option
– дать знать пользователям метода, что он может вернуть T
в форме Some[T]
или None
, чтобы обозначить отсутствие результата.
scala> def getOptString(n: Int): Option[String] =
if (n > 0) Some("A positive number!") else None
getOptString: (n: Int)Option[String]
scala> getOptString(1)
res9: Option[String] = Some(A positive number!)
scala> getOptString(-1)
res10: Option[String] = None
scala> def printOptString(optStr: Option[String]) = optStr match {
| case Some (str) => println(str)
| case None => println("none")
| }
Unit
Unit
– аналог void
в Java, используется для обозначения типа результата методов, которые не возвращают значений.
Пустые круглые скобки – значение типа Unit
.
scala> def iAmVoid(x: String): Unit = println(x)
iAmVoid: (x: String)Unit
scala> val x: Unit = ()
x: Unit = ()