Nel tuo primo esempio stai utilizzando un attributo Engine
nella tua classe Car
. Il Engine
è esposto attraverso un'interfaccia e come tale dipende dalla dichiarazione dell'interfaccia. Usando le annotazioni ti stai chiedendo un'implementazione dell'interfaccia Engine
chiamata Powerful
, per far sapere al compilatore quale implementazione esatta dell'interfaccia vuoi usare in questo caso specifico, ma il compilatore Java non ti permetterà ancora di utilizzare qualsiasi metodo al di fuori della dichiarazione dell'interfaccia.
Com'è il primo esempio meno abbinato?
Se consideriamo uno scenario in cui PowerfulEngine
contiene solo metodi che l'interfaccia Engine
fa, cioè che la classe stessa non aggiunge alcun metodo, l'accoppiamento è esattamente lo stesso in entrambi i casi, tuttavia una volta che si inizia ad aggiungere uno specifico metodi per la classe PowerfulEngine
e utilizzandoli la situazione è molto diversa.
Per spiegazioni estendiamo un po 'il tuo esempio:
interface Engine
{
void run();
}
class PowerfulEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a powerful engine.");
}
}
class RegularEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a regular engine.");
}
}
class Car
{
private Engine engine;
public void go() {
engine.run();
}
}
La classe Car
ha ottenuto un metodo go
che chiama il metodo run
di Engine
. Ora, a seconda della classe che è stata iniettata in un oggetto Car
, il sistema emetterà Esecuzione di un motore potente o Esecuzione di un motore normale.
La classe Car
sa che può utilizzare il metodo run
di Engine
perché lo fornisce e non si preoccupa della sua effettiva implementazione. Come ho detto, se decidi di sostituire la dipendenza Engine
con la dipendenza PowerfulEngine
, in questo caso non accade nulla perché stai ancora utilizzando solo il metodo dichiarato nell'interfaccia Engine
.
Ma se aggiungi un altro metodo alla classe PowerfulEngine
e lo usi, è un caso molto diverso.
Modifichiamo un po 'le classi PowerfulEngine
e Car
.
class PowerfulEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a powerful engine.");
}
public void rev() {
System.out.println("Revving a powerful engine.");
}
}
class Car
{
private PowerfulEngine engine;
public void go() {
engine.rev();
engine.run();
}
}
Che cosa succederebbe ora se decidessi di utilizzare RegularEngine
anziché PowerfulEngine
? La tua applicazione fallirebbe durante la compilazione perché la classe RegularEngine
non ha il metodo rev
, nemmeno l'interfaccia Engine
. Dovresti accedere a un metodo che non esiste.
Tutto sommato, se si dipende da una classe concreta e si utilizzano solo i metodi disponibili in una determinata interfaccia implementata dalla classe, è meglio fare affidamento sull'interfaccia anziché sulla classe direttamente, consentendo così ad altri di sapere esplicitamente: < > Ehi, in realtà non dipende dall'implementazione, non me ne importa, mi interessa solo che l'interfaccia contenga questi contratti con metodi e li posso usare.
Se hai bisogno di utilizzare un metodo specifico di una classe che non è pubblicato attraverso alcuna astrazione, non hai altra scelta che affidarti a un'implementazione concreta.