Понимание ковариации и нижней границы в Scala

Я изо всех сил пытаюсь понять ковариантность Scala в сочетании с нижними границами. Я проиллюстрирую свое замешательство в следующем фрагменте кода с двумя ошибками компиляции.

class Queue[+T]:
  def enqueue[U >: T](x: U): Queue[U] = null

class IntQueue extends Queue[Int]:
  override def enqueue[Int](x: Int): Queue[Int] =
    println(math.sqrt(x)) // 1) Found: (x : Int) Required: Double
    super.enqueue(x)      // 2) Found: Queue[Any] Required: Queue[Int]

Во-первых, есть общий класс Queue, который принимает один параметр типа с аннотацией ковариации +. Это нормально, так как я хочу выполнять такие задания, как val a: Queue[Any] = IntQueue(). Его метод enqueue имеет нижнюю границу параметра типа U >: T. Это необходимо, потому что в противном случае x был бы в контравариантном положении, допуская неприятные вещи, подобные ArrayStoreException в Array в Java. Во-вторых, существует параметризованный класс IntQueue, сгенерированный Queue с определенным типом Int.

Вопросы - почему возникают ошибки компилятора 1) и 2)

объявление 1)

  • Я полагаю, что math.sqrt определяется как def sqrt(x: Double): Double. Тип моего аргумента x относится к типу Int. В этом случае должно произойти неявное преобразование Int->Long->Float->Double. Почему компилятор не выполняет неявное преобразование?

Поскольку IntQueue может быть поднят как Queue[Any], вы должны иметь возможность получать что угодно, не только Int, на enqueue и, возможно, возвращать что-то отличное от IntQueue

Luis Miguel Mejía Suárez 15.01.2023 16:33

Включить

Dmytro Mitin 15.01.2023 17:39

Правильным переопределением для T=Int будет scastie.scala-lang.org/DmytroMitin/h1aJbMcATGmIF4XrfD3bTg/2 Теперь ошибка Found: (x : U); Required: Double

Dmytro Mitin 15.01.2023 17:53
stackoverflow.com/questions/56598220/…
Dmytro Mitin 15.01.2023 18:00
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

В вашем определении переопределяющего метода Int — это не то, что вы ожидаете. На самом деле вы определяете параметр типа с именем Int, но не ссылаетесь на «исходный» тип Int и, таким образом, «скрываете» исходный тип Int.

Вы на самом деле написали то же самое, что и:

override def enqueue[I](x: I): Queue[I] = ...

Объявите его как метод без переопределения для достижения того, что вы ожидаете:

def enqueue(x: Int): Queue[Int] = ...

поэтому, если я правильно понимаю, вы неявно указываете, какой тип параметра U находится внутри IntQueue.enqueue, объявляя тип его аргумента — x: Int

Talos 15.01.2023 20:18

Вы только неявно определяете это U >: Int, но вы не можете определить что-то более сильное, что нарушило бы контракт Queue, который заключается в том, что вы можете поставить в очередь любой другой элемент с супертипом Int. Вот почему вы не можете определить этот метод с помощью override. Это должен быть другой метод.

Gaël J 15.01.2023 20:53

вы можете определить метод с помощью override: override def enqueue[U >: Int](x: U): Queue[U] компилируется просто отлично

Talos 16.01.2023 10:26

@Talos, да, вы можете, но это не то, что вы ожидали в первую очередь: метод можно вызвать с чем угодно, что является супертипом Int, и вы вернете QUeue[U], а не IntQUeue.

Gaël J 16.01.2023 11:03

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