¿Por qué puede anularse un método que devuelve Unidad con un método que devuelve Cadena cuando los tipos de retorno no se proporcionan explícitamente?

11

Estaba trabajando en los ejemplos de código del capítulo sobre Rasgos en la programación en Scala Edition1 https://www.artima.com/pins1ed/traits.html

y me encontré con un comportamiento extraño debido a mi error tipográfico. Mientras se sobreescriben el método de un rasgo a continuación fragmento de código no da ningún error de compilación si bien los tipos de retorno del método reemplazado es diferente Unitvs String. Pero al llamar al método en un objeto, devuelve Unit pero no imprime nada.

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 = ()

Pero cuando doy el tipo de retorno explícito en el método anulado, da un error de compilación:

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

¿Alguien puede ayudar a explicar por qué no hay error de compilación en el primer caso?

Shanil
fuente
El compilador ha impreso una sugerencia válida de que intenta anular un método que tiene un tipo de resultado diferente.
Andriy Plokhotnyuk
Sí, de hecho, pero mi pregunta es por qué pasó por el compilador en el primer caso
Shanil el
1
Como regla general, siempre sea explícito sobre los tipos de retorno_ (especialmente en las API públicas) _. La inferencia de tipos es excelente para las variables locales, nada más.
Luis Miguel Mejía Suárez

Respuestas:

8

Cuando el tipo esperado es Unit, cualquier valor puede ser aceptado :

Descarte de valor

Si etiene algún tipo de valor y el tipo esperado es Unit, ese convierte al tipo esperado incrustándolo en el término { e; () }.

Alexey Romanov
fuente
6

mi pregunta es por qué pasó por el compilador en el primer caso

Cuando no especificó el tipo de retorno explícitamente, se infirió por el tipo que necesita tener para overrideque funcione.

Eso resultó ser Unit.

Como Stringse pueden asignar valores (el valor de la expresión que constituye el cuerpo de la función) Unit, el compilador está contento.

Thilo
fuente
1
Sin embargo, lo que quiero saber ahora es por qué Stringse rechazó el tipo de retorno explícito . En Java (y creo que también en Scala), se le permite limitar el tipo de retorno al anular. Por ejemplo, cuando el método padre regresa Number, usted puede regresar Integer. Quizás void/ Unites especial.
Thilo
1
Esto compila, por ejemplo:trait Philosophical { def philosophize : Number = 1 } class Frog extends Philosophical { override def philosophize : Integer = 2 }
Thilo
2
Puede limitar el tipo de retorno a un subtipo; pero no puede limitarlo a un tipo que solo se pueda convertir implícitamente al tipo de retorno del método reemplazado. Stringto Unites más como el segundo, incluso si no es exactamente eso.
Alexey Romanov
2
En el nivel de bytecode tampoco hay un tipo de retorno de estrechamiento, en realidad hay dos métodos en Frog: def philosophize : Integery def philosophize : Number. El segundo en realidad anula el Philosophicalmétodo (y llama al primero). Ciertamente, se podría hacer lo mismo por voidcualquier otra cosa, los diseñadores simplemente decidieron no hacerlo.
Alexey Romanov
2
1. Java hace lo mismo. 2. Sí, en bytecode puedes. Consulte, por ejemplo, stackoverflow.com/questions/18655541/… y stackoverflow.com/questions/58065680/… .
Alexey Romanov