Что такое пустое/нулевое значение для универсального типа в Scala

Привет, я недавно изучаю 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 = ""
//}

Но он запускается с исключением во время инициации.

override def head: Nothing = throw new NoSuchElementException должен работать так, как вы ожидали.
Preston 28.11.2022 08:16

Еще лучше моделировать List с помощью ADT, а не пытаться использовать методы ООП: scastie.scala-lang.org/BalmungSan/FQ13Ncd0T26B1VujWTyP3A

Luis Miguel Mejía Suárez 28.11.2022 14:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
105
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Возвращаться 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.

Другие вопросы по теме