Привет, я недавно изучаю Scala, и меня смущает нуль/ничего в Scala, в следующем коде я реализую связанный с клиентом список с общим типом и практикуюсь с дисперсией.
Определил мой абстрактный класс
/**
* head = first element of the list
* tail = remainder of the list
* isEmpty = is this list empty
* add(int) => new list with this element added
* toString => a string representation of the list
*/
abstract class MyList[+T] {
val head: T
val tail: MyList[T]
val isEmpty: Boolean
def add[A >: T](n: A): MyList[A]
def printElements: String
override def toString: String = s"[$printElements]"
}
Два типа класса узла — Пустой и Минусы
class Empty[+T] extends MyList[T] {
override val head: T = ??? // what should I implement at here???
override val tail: MyList[T] = null
override val isEmpty: Boolean = true
override def add[A >: T](elem: A): MyList[A] = new Cons(elem, this)
override def printElements: String = ""
}
class Cons[+T](h: T, t: MyList[T]) extends MyList[T] {
override val head: T = h
override val tail: MyList[T] = t
override val isEmpty: Boolean = false
override def add[A >: T](elem: A): MyList[A] = new Cons(elem, this)
override def printElements: String = {
if (tail.isEmpty) s"$head"
else s"$head, ${tail.printElements}"
}
}
Вот мой тест
object ListTest2 extends App {
val listOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Empty[Int]))
val listOfString: MyList[String] = new Cons("1", new Cons("2", new Empty[String]))
println(listOfIntegers)
println(listOfString)
}
Я попытался определить object Empty для последнего узла в этом связанном списке.
//object Empty extends MyList[Nothing] {
// override val head: Nothing = throw new NoSuchElementException
// override val tail: MyList[Nothing] = null
// override val isEmpty: Boolean = true
//
// override def add[B >: Nothing](elem: B): MyList[B] = new Cons(elem, Empty)
// override def printElements: String = ""
//}
Но он запускается с исключением во время инициации.
Еще лучше моделировать List с помощью ADT, а не пытаться использовать методы ООП: scastie.scala-lang.org/BalmungSan/FQ13Ncd0T26B1VujWTyP3A





Возвращаться null из tail не очень хорошая идея. Это может привести к NPE. Лучше пораньше провалиться, поэтому просто бросайте (если это ваша семантика метода). Лучшая семантика - вернуть пустой список.
head также может бросить. Это единственный способ создать значение произвольного типа T. Если вы можете изменить подпись, то лучше вернуть Option[T] т.е. None вместо null или исключения.
Обычно в трейте или абстрактном классе абстрактные члены (head, tail, isEmpty) должны быть def. Являются ли они на самом деле def, val или lazy val, это детали реализации (и должны быть указаны в наследниках).
Я попытался определить объект Empty для последнего узла в этом связанном списке... Но он запускается с исключением во время инициации.
Вы можете избежать исключения, если сделаете headlazy val (или def)
override lazy val head: Nothing = throw new NoSuchElementException
def можно переопределить с помощью def, val, lazy val.
override def head: Nothing = throw new NoSuchElementExceptionдолжен работать так, как вы ожидали.