Ricerca…


introduzione

Il polimorfismo è uno dei principali concetti OOP (programmazione orientata agli oggetti). La parola Polymorphism deriva dalle parole greche "poly" e "morph". Poly significa "molti" e metamorfosi significa "forme" (molte forme).

Esistono due modi per eseguire il polimorfismo. Sovraccarico del metodo e sovrascrittura del metodo .

Osservazioni

Interfaces sono un altro modo per ottenere il polimorfismo in Java, a parte l'ereditarietà basata sulla classe. Le interfacce definiscono un elenco di metodi che costituiscono l'API del programma. Le classi devono implement interface ignorando tutti i suoi metodi.

Sovraccarico del metodo

L'overloading del metodo , noto anche come overloading delle funzioni , è la capacità di una classe di avere più metodi con lo stesso nome, a condizione che differiscano per numero o tipo di argomenti.

Il compilatore verifica la firma del metodo per l'overloading del metodo.

La firma del metodo consiste di tre cose:

  1. Nome del metodo
  2. Numero di parametri
  3. Tipi di parametri

Se questi tre sono gli stessi per due metodi in una classe, il compilatore genera un errore di metodo duplicato .

Questo tipo di polimorfismo viene chiamato polimorfismo statico o tempo di compilazione perché il metodo appropriato da chiamare viene deciso dal compilatore durante il tempo di compilazione in base all'elenco degli argomenti.

class Polymorph {

    public int add(int a, int b){
        return a + b;
    }
    
    public int add(int a, int b, int c){
        return a + b + c;
    }

    public float add(float a, float b){
        return a + b;
    }

    public static void main(String... args){
        Polymorph poly = new Polymorph();
        int a = 1, b = 2, c = 3;
        float d = 1.5, e = 2.5;

        System.out.println(poly.add(a, b));
        System.out.println(poly.add(a, b, c));
        System.out.println(poly.add(d, e));
    }

}

Ciò comporterà:

2
6
4.000000

I metodi di overload possono essere statici o non statici. Questo inoltre non influisce sul sovraccarico del metodo.

public class Polymorph {

    private static void methodOverloaded()
    {
        //No argument, private static method
    }
 
    private int methodOverloaded(int i)
    {
        //One argument private non-static method
        return i;
    }
 
    static int methodOverloaded(double d)
    {
        //static Method
        return 0;
    }
 
    public void methodOverloaded(int i, double d)
    {
        //Public non-static Method
    }
}

Inoltre, se si modifica il tipo di metodo restituito, non è possibile ottenerlo come overload del metodo.

public class Polymorph {  

void methodOverloaded(){
    //No argument and No return type
}

int methodOverloaded(){
    //No argument and int return type 
    return 0;
}

Metodo Overriding

Il metodo che sovrascrive è la capacità dei sottotipi di ridefinire (scavalcare) il comportamento dei loro supertipi.

In Java, questo si traduce in sottoclassi che sovrascrivono i metodi definiti nella super classe. In Java, tutte le variabili non primitive sono in realtà references , che sono simili ai puntatori alla posizione dell'oggetto reale in memoria. I references hanno solo un tipo, che è il tipo con cui sono stati dichiarati. Tuttavia, possono puntare a un oggetto del tipo dichiarato o di uno qualsiasi dei suoi sottotipi.

Quando un metodo viene chiamato su un reference , viene richiamato il metodo corrispondente dell'oggetto reale che viene puntato .

class SuperType {
    public void sayHello(){
        System.out.println("Hello from SuperType");
    }

    public void sayBye(){
        System.out.println("Bye from SuperType");
    }
}

class SubType extends SuperType {
    // override the superclass method
    public void sayHello(){
        System.out.println("Hello from SubType");
    }
}

class Test {
    public static void main(String... args){
        SuperType superType = new SuperType();
        superType.sayHello(); // -> Hello from SuperType

        // make the reference point to an object of the subclass
        superType = new SubType();
        // behaviour is governed by the object, not by the reference
        superType.sayHello(); // -> Hello from SubType

        // non-overridden method is simply inherited
        superType.sayBye(); // -> Bye from SuperType
    }
}

Regole da tenere a mente

Per sovrascrivere un metodo nella sottoclasse, il metodo di sovrascrittura (cioè quello nella sottoclasse) DEVE AVERE :

  • stesso nome
  • stesso tipo di ritorno in caso di primitive (una sottoclasse è consentita per le classi, questo è anche noto come tipi di ritorno covarianti).
  • stesso tipo e ordine dei parametri
  • può lanciare solo quelle eccezioni dichiarate nella clausola di tiro del metodo della superclasse o delle eccezioni che sono sottoclassi delle eccezioni dichiarate. Può anche scegliere di non lanciare alcuna eccezione. I nomi dei tipi di parametri non contano. Ad esempio, void methodX (int i) è uguale a void methodX (int k)
  • Non siamo in grado di sovrascrivere i metodi definitivi o statici. L'unica cosa che possiamo fare cambia solo il metodo body.

Aggiunta di comportamenti aggiungendo classi senza toccare il codice esistente

import java.util.ArrayList;
import java.util.List;

import static java.lang.System.out;

public class PolymorphismDemo {

    public static void main(String[] args) {
        List<FlyingMachine> machines = new ArrayList<FlyingMachine>();
        machines.add(new FlyingMachine());
        machines.add(new Jet());
        machines.add(new Helicopter());
        machines.add(new Jet());

        new MakeThingsFly().letTheMachinesFly(machines);
    }
}

class MakeThingsFly {
    public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

class FlyingMachine {
    public void fly() {
        out.println("No implementation");
    }
}

class Jet extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start, taxi, fly");
    }

    public void bombardment() {
        out.println("Fire missile");
    }
}

class Helicopter extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start vertically, hover, fly");
    }
}

Spiegazione

a) La classe MakeThingsFly può funzionare con tutto ciò che è di tipo FlyingMachine .

b) Il metodo letTheMachinesFly funziona anche senza alcuna modifica (!) quando si aggiunge una nuova classe, ad esempio PropellerPlane :

public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

Questo è il potere del polimorfismo. Puoi implementare il principio open-closed con esso.

Funzioni virtuali

I metodi virtuali sono metodi in Java non statici e senza la parola chiave Final in primo piano. Tutti i metodi di default sono virtuali in Java. I Metodi Virtuali svolgono ruoli importanti nel Polymorphism perché le classi di bambini in Java possono sovrascrivere i metodi delle loro classi genitore se la funzione che viene sovrascritta non è statica e ha la stessa firma di metodo.

Esistono, tuttavia, alcuni metodi che non sono virtuali. Ad esempio, se il metodo è dichiarato privato o con la parola chiave final, il metodo non è Virtual.

Considera il seguente esempio modificato di ereditarietà con metodi virtuali da questo post StackOverflow Come funzionano le funzioni virtuali in C # e Java? :

public class A{
    public void hello(){
        System.out.println("Hello");
    }
    
    public void boo(){
        System.out.println("Say boo");

    }
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

Se invochiamo la classe B e chiamiamo hello () e boo (), otterremmo "No" e "Say haha" come output risultante perché B sovrascrive gli stessi metodi da A. Anche se l'esempio sopra è quasi identico a sovrascrittura del metodo, è importante capire che i metodi in classe A sono tutti, per impostazione predefinita, virtuali.

Inoltre, possiamo implementare metodi virtuali usando la parola chiave abstract. I metodi dichiarati con la parola chiave "abstract" non hanno una definizione di metodo, il che significa che il corpo del metodo non è ancora stato implementato. Considera di nuovo l'esempio sopra, eccetto che il metodo boo () è dichiarato astratto:

public class A{
   public void hello(){
        System.out.println("Hello");
    }
    
    abstract void boo();
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

Se invochiamo boo () da B, l'output sarà ancora "Say haha" poiché B eredita il metodo astratto boo () e rende boo () output "Say haha".

Fonti utilizzate e ulteriori letture:

Come funzionano le funzioni virtuali in C # e Java?

Dai un'occhiata a questa ottima risposta che fornisce informazioni molto più complete sulle funzioni virtuali:

Puoi scrivere funzioni / metodi virtuali in Java?

Polimorfismo e diversi tipi di override

Dal tutorial di java

La definizione di polimorfismo del dizionario fa riferimento a un principio in biologia in cui un organismo o una specie possono avere molte forme o stadi diversi. Questo principio può essere applicato anche alla programmazione orientata agli oggetti e ai linguaggi come il linguaggio Java. Sottoclassi di una classe possono definire i propri comportamenti univoci e tuttavia condividere alcune delle stesse funzionalità della classe genitore.

Dai un'occhiata a questo esempio per capire i diversi tipi di sovrascrittura.

  1. La classe base non fornisce implementazione e la sottoclasse deve sovrascrivere il metodo completo - (abstract)
  2. La classe base fornisce l'implementazione predefinita e la sottoclasse può modificare il comportamento
  3. La super.methodName() aggiunge l'estensione all'implementazione della classe base chiamando super.methodName() come prima istruzione
  4. La classe base definisce la struttura dell'algoritmo (metodo Template) e la sottoclasse sovrascrive una parte dell'algoritmo

snippet di codice:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;
    
    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */
    
    protected abstract void initializeGame();
    
    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }
    
    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");    
        while (runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);    
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);
    
    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if (currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:" + p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:" + p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{
        
            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);
                        
            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);
        
        }catch(Exception err){
            err.printStackTrace();
        }        
    }
}

produzione:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:

Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow