Por que um método que retorna a Unidade pode ser substituído pelo método que retorna String quando os tipos de retorno não são explicitamente fornecidos?

11

Eu estava trabalhando nos exemplos de código do capítulo Traits in Programming in Scala Edition1 https://www.artima.com/pins1ed/traits.html

e me deparei com um comportamento estranho por causa do meu erro de digitação. Enquanto substituindo método de um traço abaixo trecho de código não dá qualquer erro de compilação, embora os tipos de retorno do método substituído é diferente Unitvs String. Mas, ao chamar o método em um objeto, ele retorna Unit, mas não 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 = ()

Mas quando eu dou o tipo de retorno explícito no método substituído, ele gera um erro de compilação:

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

Alguém pode ajudar a explicar por que nenhum erro de compilação no primeiro caso.

Shanil
fonte
O compilador imprimiu uma dica válida de que você tenta substituir um método que possui um tipo de resultado diferente.
Andriy Plokhotnyuk 02/12/19
Sim, de fato, mas a minha pergunta é por isso que tenho através do compilador no 1º caso
Shanil
11
Como regra geral, sempre seja explícito sobre os tipos de retorno_ (especialmente em APIs públicas) _. A inferência de tipo é ótima para variáveis ​​locais, nada mais.
Luis Miguel Mejía Suárez

Respostas:

8

Quando o tipo esperado é Unit, qualquer valor pode ser aceito :

Descarte de valor

Se etiver algum tipo de valor e o tipo esperado for Unit, eé convertido no tipo esperado incorporando-o ao termo { e; () }.

Alexey Romanov
fonte
6

minha pergunta é por que passou pelo compilador no 1º caso

Quando você não especificou o tipo de retorno explicitamente, foi inferido pelo tipo que ele precisa ter para que o overridetrabalho funcione.

Isso acabou por ser Unit.

Como Stringvalores (o valor da expressão que compõe o corpo da função) podem ser atribuídos Unit, o compilador fica feliz.

Thilo
fonte
11
O que eu quero saber agora, porém, é por que o tipo de retorno explícito Stringfoi rejeitado. Em Java (e também penso em Scala), você pode restringir o tipo de retorno ao substituir. Por exemplo, quando o método pai retornar Number, você poderá retornar Integer. Talvez void/ Unitseja especial.
Thilo
11
Isso compila, por exemplo:trait Philosophical { def philosophize : Number = 1 } class Frog extends Philosophical { override def philosophize : Integer = 2 }
Thilo
2
Você pode restringir o tipo de retorno a um subtipo; mas você não pode restringi-lo a um tipo que só pode ser implicitamente convertido no tipo de retorno do método substituído. Stringto Unité mais parecido com o segundo, mesmo que não seja exatamente isso.
Alexey Romanov
2
No nível de bytecode, também não há um tipo de retorno restrito; na verdade, existem dois métodos em Frog: def philosophize : Integere def philosophize : Number. O segundo substitui o Philosophicalmétodo do (e chama o primeiro). O mesmo certamente poderia ser feito para void/ qualquer outra coisa, os designers simplesmente decidiram não fazê-lo.
Alexey Romanov
2
1. Java faz o mesmo. 2. Sim, no bytecode você pode. Consulte, por exemplo, stackoverflow.com/questions/18655541/… e stackoverflow.com/questions/58065680/… .
Alexey Romanov