Java Language
Polimorfismo
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:
- Nome del metodo
- Numero di parametri
- 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.
- La classe base non fornisce implementazione e la sottoclasse deve sovrascrivere il metodo completo - (abstract)
- La classe base fornisce l'implementazione predefinita e la sottoclasse può modificare il comportamento
- La
super.methodName()
aggiunge l'estensione all'implementazione della classe base chiamandosuper.methodName()
come prima istruzione - 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: