Почему метод, возвращающий Unit, может быть переопределен методом, возвращающим String, если типы возврата не указаны явно?

11

Я работал над примерами кода из главы «Особенности в программировании в Scala Edition1» https://www.artima.com/pins1ed/traits.html

и наткнулся на странное поведение из-за моей опечатки. Несмотря на то, что переопределенный метод признака ниже фрагмента кода не дает никакой ошибки компиляции, хотя возвращаемые типы переопределенного метода отличаются Unitот String. Но после вызова метода объекта он возвращает Unit, но ничего не печатает.

trait Philosophical {
    def philosophize = println("I consume memory, therefore I am!")
}

class Frog extends Philosophical {
  override def toString = "green"
  override def philosophize = "It aint easy to be " + toString + "!"
}

val frog = new Frog
//frog: Frog = green

frog.philosophize
// no message printed on console

val f = frog.philosophize
//f: Unit = ()

Но когда я даю явный тип возвращаемого значения в переопределенном методе, он выдает ошибку компиляции:

class Frog extends Philosophical {
  override def toString = "green"
  override def philosophize: String = "It aint easy to be " + toString + "!"
}
         override def philosophize: String = "It aint easy to be " + toString +
                      ^
On line 3: error: incompatible type in overriding
       def philosophize: Unit (defined in trait Philosophical);
        found   : => String
        required: => Unit

Может кто-нибудь помочь объяснить, почему нет ошибки компиляции в первом случае.

Shanil
источник
Компилятор напечатал правильный совет, что вы пытаетесь переопределить метод, который имеет другой тип результата.
Андрей Плохотнюк
Да, действительно, но мой вопрос, почему он прошел через компилятор в 1-м случае
Shanil
1
Эмпирическое правило, всегда указывайте возвращаемые типы_ (особенно в открытых API) _. Вывод типа отлично подходит для локальных переменных, больше ничего.
Луис Мигель Мехия Суарес

Ответы:

8

Когда ожидается тип Unit, любое значение может быть принято :

Отбрасывание значения

Если eимеет некоторый тип значения и ожидаемый тип Unit, eпреобразуется в ожидаемый тип путем встраивания его в термин { e; () }.

Алексей романов
источник
6

у меня вопрос почему он попал через компилятор в 1-м случае

Когда вы не указали тип возвращаемого значения явно, он был выведен по типу, необходимому для overrideработы.

Это оказалось Unit.

Поскольку Stringзначения (значение выражения, составляющего тело функции) могут быть назначены Unit, компилятор доволен.

Тило
источник
1
Однако сейчас я хочу узнать, почему явный тип возвращаемого значения Stringбыл отклонен. В Java (и я думаю, что и в Scala) вам разрешено сужать тип возвращаемого значения при переопределении. Например, когда родительский метод возвращается Number, вы можете вернуться Integer. Возможно void/ Unitособенный.
Thilo
1
Это компилирует, например:trait Philosophical { def philosophize : Number = 1 } class Frog extends Philosophical { override def philosophize : Integer = 2 }
Thilo
2
Вы можете сузить тип возвращаемого значения до подтипа; но вы не можете сузить его до типа, который может быть неявно преобразован только в возвращаемый тип переопределенного метода. Stringчтобы Unitбольше похож на второй, даже если это не совсем то.
Алексей Романов
2
На уровне байт-кода также нет сужающегося возвращаемого типа, на самом деле есть два метода Frog: def philosophize : Integerи def philosophize : Number. Второй фактически переопределяет Philosophicalметод (и вызывает первый). То же самое можно сделать и для voidвсего остального, дизайнеры просто решили этого не делать.
Алексей Романов
2
1. Java делает то же самое. 2. Да, в байт-коде вы можете. См., Например, stackoverflow.com/questions/18655541/… и stackoverflow.com/questions/58065680/… .
Алексей Романов