Dlaczego metodę zwracającą jednostkę można zastąpić metodą zwracającą ciąg znaków, gdy typy zwracane nie są jawnie podane?

11

Pracowałem nad przykładami kodu z rozdziału dotyczącego cech w programowaniu w Scala Edition1 https://www.artima.com/pins1ed/traits.html

i natknąłem się na dziwne zachowanie z powodu mojej literówki. Podczas gdy metoda przesłaniania cechy poniżej fragmentu kodu nie daje żadnego błędu kompilacji, chociaż typy zwracane przez przesłoniętą metodę są inne Unitniż String. Ale po wywołaniu metody na obiekcie zwraca Unit, ale nic nie drukuje.

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

Ale gdy podaję jawny typ zwrotu w przesłoniętej metodzie, pojawia się błąd kompilacji:

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

Czy ktoś może wyjaśnić, dlaczego w pierwszym przypadku nie wystąpił błąd kompilacji.

Shanil
źródło
Kompilator wydrukował poprawną wskazówkę, że próbujesz zastąpić metodę, która ma inny typ wyniku.
Andriy Plokhotnyuk
Tak, rzeczywiście, ale moje pytanie brzmi: dlaczego przeszedł przez kompilator w pierwszym przypadku
Shanil
1
Ogólna zasada: zawsze zwracaj uwagę na typy zwrotów_ (szczególnie w publicznych interfejsach API) _. Wnioskowanie typu jest świetne dla zmiennych lokalnych, nic więcej.
Luis Miguel Mejía Suárez,

Odpowiedzi:

8

Gdy oczekiwanym typem jest Unit, można zaakceptować dowolną wartość :

Odrzucanie wartości

Jeśli ema jakiś typ wartości, a oczekiwanym typem jest Unit, ejest konwertowany na typ oczekiwany przez osadzenie go w terminie { e; () }.

Aleksiej Romanow
źródło
6

moje pytanie brzmi: dlaczego przeszedł przez kompilator w pierwszym przypadku

Jeśli nie określono wyraźnie typu zwrotu, został on wywnioskowany na podstawie typu, jaki musi on mieć overridedo działania.

Okazało się, że tak Unit.

Ponieważ Stringmożna przypisać wartości (wartość wyrażenia tworzącego ciało funkcji) Unit, kompilator jest szczęśliwy.

Thilo
źródło
1
Teraz jednak chcę wiedzieć, dlaczego jawny typ zwrotu Stringzostał odrzucony. W Javie (i myślę również w Scali) możesz zawęzić typ zwracany podczas przesłonięcia. Na przykład po powrocie metody nadrzędnej Numbermożesz zwrócić Integer. Może void/ Unitjest wyjątkowy.
Thilo,
1
To kompiluje, na przykład:trait Philosophical { def philosophize : Number = 1 } class Frog extends Philosophical { override def philosophize : Integer = 2 }
Thilo
2
Możesz zawęzić typ zwracany do podtypu; ale nie można zawęzić go do typu, który można niejawnie przekonwertować na typ zwracany przez przesłoniętą metodę. Stringdo Unitbardziej jakby drugim, nawet jeśli nie jest to dokładnie to.
Aleksiej Romanow
2
Na poziomie kodu bajtowego nie ma również zwężającego się typu powrotu, w rzeczywistości istnieją dwie metody Frog: def philosophize : Integeri def philosophize : Number. Drugi faktycznie zastępuje Philosophicalmetodę (i wywołuje pierwszą). To samo można z pewnością zrobić dla void/ czegokolwiek innego, projektanci po prostu postanowili tego nie robić.
Aleksiej Romanow
2
1. Java robi to samo. 2. Tak, w kodzie bajtowym możesz. Patrz np stackoverflow.com/questions/18655541/... i stackoverflow.com/questions/58065680/... .
Aleksiej Romanow