2013-08-21 11 views
7

Sono confuso per quanto riguarda la compatibilità con le versioni precedenti quando si aggiunge un metodo con un'implementazione predefinita a un tratto. Come:È l'aggiunta di un metodo tratto con l'implementazione che interrompe la compatibilità con le versioni precedenti?

versione precedente

trait Foo 

nuova versione

trait Foo { 
    def verifyConsistency: Option[String] = ??? // provide default implementation 
} 

Le Migration Manager rapporti questa aggiunta come l'incompatibilità binaria. È corretto?

risposta

10

Beh, sì, è corretto.

Quando si definisce tratto Foo, sarà sotto il cofano creare sia (JVM) Interfaccia Foo e (JVM) classe Foo$class con tutte le implementazioni dei metodi definiti come metodi statici. Il codice Java corrispondente sarebbe simile a qualcosa di simile (per la nuova definizione a di Foo):

interface Foo { 
    Option<String> verifyConsistency(); 
} 

class Foo$class { 
    static Option<String> verifyConsistency(Foo self) { 
    Predef.???(); 
    } 
} 

Quando si mescolano Foo in una classe concreta Bar, ciò che accade a livello di JVM è che Bar estende l'interfaccia Foo, e implementa il metodo verifyConsistency inoltrando semplicemente la chiamata a Foo$class:

class Bar implements Foo { 
    Option<String> verifyConsistency() { 
    return Foo$class.verifyConsistency(this); // simple forwarding 
    } 
} 

il motivo per cui lo si fa in questo modo è che il modello a oggetti JVM non supporta l'ereditarietà multipla. Le implementazioni dei tratti non possono essere semplicemente inserite in classi da cui potresti estendere, perché puoi sempre estendere una sola classe sulla JVM.

Il take away di questa situazione è che ogni volta che una classe concreta mescola un tratto, la classe definisce i metodi "stub" per ogni membro del tratto (quei metodi semplicemente inoltrano l'implementazione effettiva, che è un metodo statico).

Una conseguenza è che se si aggiunge un nuovo metodo a un tratto, anche se si definisce un'implementazione non è sufficiente: le classi concrete che mescolano il tratto devono essere ricompilate (in modo che venga aggiunto uno stub per il nuovo metodo alla classe). Se non ricompilate quelle classi, il vostro programma non riuscirà a funzionare, poiché ora avreste una classe che è presumibilmente concreta (non astratta) E estenderete l'interfaccia corrispondente, ma in realtà mancherete l'implementazione per il nuovo metodo.

Nel tuo caso questo significa avere classi concrete che estendono l'interfaccia Foo ma non hanno alcuna implementazione per verifyConsistency.

Quindi l'incompatibilità binaria.

+1

Ok, grazie. Ho pensato che l'intera cerimonia attorno ai tratti era che si potevano aggiungere metodi e, a patto di fornire un'implementazione predefinita, non si deve ricompilare tutto. Quindi immagino di essermi sbagliato :-( –