ProgrammazioneSviluppatore Java

Cos'è il polimorfismo in Java, come viene realizzato e a cosa serve?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della questione:

Il polimorfismo è uno dei principi chiave della programmazione orientata agli oggetti (OOP), supportato in Java sin dalla sua creazione. Permette a un oggetto di comportarsi in modi diversi a seconda del suo tipo effettivo durante l'esecuzione del programma, anche se si utilizza un riferimento di tipo base.

Problema:

Senza polimorfismo, il codice diventa rigido, spesso appaiono costruzioni ridondanti di tipo switch-case o if-else per lavorare con oggetti di diversi tipi. Questo complica la manutenzione e l'estensione del codice. Il polimorfismo risolve la necessità di scrivere codice ripetitivo per tipi diversi.

Soluzione:

Il polimorfismo in Java è realizzato attraverso l'ereditarietà e le interfacce. Permette di:

  • un riferimento di tipo genitore di puntare a un oggetto di tipo figlio;
  • chiamare metodi sovrascritti senza conoscere il tipo specifico dell'oggetto durante la compilazione.

Esempio di codice:

class Animal { void speak() { System.out.println("Animal speaks"); } } class Dog extends Animal { @Override void speak() { System.out.println("Dog barks"); } } class Cat extends Animal { @Override void speak() { System.out.println("Cat meows"); } } public class PolyDemo { public static void main(String[] args) { Animal a1 = new Dog(); Animal a2 = new Cat(); a1.speak(); // Dog barks a2.speak(); // Cat meows } }

Caratteristiche chiave:

  • Consente di estendere e modificare i sistemi senza modificare il codice esistente.
  • Garantisce un accoppiamento debole tra i componenti.
  • Attraverso il polimorfismo vengono realizzati i pattern di progettazione (ad esempio, Strategy, Command).

Domande trabocchetto.

Qual è la differenza tra overloading e overriding dei metodi nel polimorfismo?

L'overloading è la definizione di più metodi con lo stesso nome ma con firme diverse all'interno della stessa classe. L'overriding è la definizione di un metodo nella sottoclasse con la stessa firma di quella nella classe genitore.

class Example { void foo(int x) {} void foo(String y) {} // questo è overloading } class Base { void foo() {} } class Child extends Base { @Override void foo() {} // questo è overriding }

Può esistere polimorfismo senza ereditarietà?

Nel senso classico, in Java, no: il polimorfismo richiede l'esistenza di una gerarchia di ereditarietà o di un'interfaccia implementata.

È possibile chiamare metodi della sottoclasse da un riferimento alla classe genitore?

È possibile chiamare solo metodi definiti nella classe genitore o sovrascritti nella sottoclasse. I metodi che esistono solo nella sottoclasse non possono essere chiamati senza un cast di tipo.

Animal a = new Dog(); a.speak(); // è possibile // a.fetch(); // errore di compilazione, anche se Dog ha il metodo fetch()

Errori tipici e anti-pattern

  • Sovrascrivere metodi senza l'annotazione @Override — il compilatore non rileverà l'errore di incongruenza della firma.
  • Casting di tipo senza verifica tramite instanceof, che provoca ClassCastException.
  • Utilizzare il polimorfismo dove sarebbe più semplice ricorrere alla composizione.

Esempio dalla vita reale

Caso negativo

In un progetto sono state implementate classi Dog, Cat, Cow, ma il codice utilizzato operava direttamente con i tipi, chiamando metodi tramite cast espliciti e instanceof:

Vantaggi:

  • Implementazione rapida di nuove specie di animali.

Svantaggi:

  • Espansione di if-else.
  • Violazione dei principi OOP.
  • Complessità nella scalabilità.

Caso positivo

Ereditarietà di Animal con speak() virtuale. Tutta l'interazione avviene tramite il tipo base Animal.

Vantaggi:

  • Flessibilità nell'aggiunta di nuove specie.
  • Semplicità nei test.

Svantaggi:

  • Più complicato quando gli oggetti non sono affatto correlati, a volte la composizione sarebbe stata preferibile.