Sök…


Introduktion

Polymorfism är ett av de viktigaste OOP (objektorienterade programmering) koncept. Polymorphism ordet härstammar från de grekiska orden "poly" och "morphs". Poly betyder "många" och morfer betyder "former" (många former).

Det finns två sätt att utföra polymorfism. Metodöverbelastning och metodöverskridande .

Anmärkningar

Interfaces är ett annat sätt att uppnå polymorfism i Java, förutom klassbaserad arv. Gränssnitt definierar en lista över metoder som utgör programmets API. Klasser måste implement ett interface genom att åsidosätta alla dess metoder.

Metod överbelastning

Metodöverbelastning , även känd som funktionsöverbelastning , är en klass förmåga att ha flera metoder med samma namn, förutsatt att de skiljer sig antingen i antalet eller typ av argument.

Compiler kontrollerar metodsignatur för metodöverbelastning.

Metodsignatur består av tre saker -

  1. Metodnamn
  2. Antal parametrar
  3. Parametrar

Om dessa tre är samma för två metoder i en klass, kastar kompilatorn duplikatmetodfel .

Denna typ av polymorfism kallas statisk eller kompilera tidspolymorfism eftersom den lämpliga metoden som ska kallas beslutas av kompilatorn under sammanställningstiden baserat på argumentlistan.

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));
    }

}

Detta kommer att resultera i:

2
6
4.000000

Överbelastade metoder kan vara statiska eller icke-statiska. Detta påverkar inte heller överbelastning av metoden.

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
    }
}

Om du ändrar metodens returtyp kan vi inte få den som överbelastning av metoden.

public class Polymorph {  

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

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

Metodöverskridande

Överskridande av metoder är subtypernas förmåga att omdefiniera (åsidosätta) beteendet hos deras supertyper.

I Java översätts detta till underklasser som åsidosätter metoderna som definieras i superklassen. I Java är alla icke-primitiva variabler faktiska references , som liknar pekare till platsen för det faktiska objektet i minnet. references bara en typ, vilket är den typ de förklarades med. De kan emellertid peka på ett objekt av antingen deras deklarerade typ eller någon av dess subtyper.

När en metod kallas på en reference åberopas motsvarande metod för det faktiska objektet som pekas på .

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
    }
}

Regler att tänka på

Att åsidosätta en metod i underklassen, den tvingande metoden (dvs. den i underklassen) måste ha:

  • samma namn
  • samma returtyp för primitiv (en underklass tillåts för klasser, detta kallas också kovarianta returtyper).
  • samma typ och parameterordning
  • det får bara kasta de undantag som deklareras i kastklausulen för superklassens metod eller undantag som är underklasser för de deklarerade undantagen. Den kan också välja att INTE kasta något undantag. Namnen på parametertyperna spelar ingen roll. Till exempel är ogiltig metodX (int i) densamma som ogiltig metodX (int k)
  • Vi kan inte åsidosätta slutliga eller statiska metoder. Det enda vi kan göra ändrar bara metodkroppen.

Lägga till beteende genom att lägga till klasser utan att beröra befintlig kod

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");
    }
}

Förklaring

a) MakeThingsFly kan arbeta med allt som är av typen FlyingMachine .

b) Metoden letTheMachinesFly fungerar också utan någon förändring (!) när du lägger till en ny klass, till exempel PropellerPlane :

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

Det är kraften i polymorfism. Du kan implementera den öppna-stängda principen med den.

Virtuella funktioner

Virtuella metoder är metoder i Java som är icke-statiska och utan nyckelordet Final framför. Som standard är alla metoder virtuella i Java. Virtuella metoder spelar viktiga roller i polymorfism eftersom barnklasser i Java kan åsidosätta sina föräldraklassers metoder om funktionen som åsidosätts är icke-statisk och har samma metodsignatur.

Det finns dock vissa metoder som inte är virtuella. Till exempel, om metoden förklaras privat eller med nyckelordet final, är metoden inte Virtuell.

Tänk på följande modifierade exempel på arv med virtuella metoder från detta StackOverflow-inlägg. Hur fungerar virtuella funktioner i C # och 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");

    }
}

Om vi åberopar klass B och kallar hej () och boo (), skulle vi få "Nej" och "Säg haha" som den resulterande utgången eftersom B åsidosätter samma metoder från A. Trots att exemplet ovan är nästan exakt samma som åsidosättande av metoden, är det viktigt att förstå att metoderna i klass A alla är som standard virtuella.

Dessutom kan vi implementera virtuella metoder med det abstrakta nyckelordet. Metoder som deklareras med nyckelordet "abstrakt" har ingen metoddefinition, vilket betyder att metodens kropp ännu inte är implementerad. Tänk exemplet ovanifrån igen, förutom att boo () -metoden förklaras abstrakt:

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");

    }
}

Om vi åberopar boo () från B kommer utgången fortfarande att vara "Say haha" eftersom B ärver den abstrakta metoden boo () och skapar boo () output "Say haha".

Källor som används och vidare avläsningar:

Hur fungerar virtuella funktioner i C # och Java?

Kolla in det här fantastiska svaret som ger en mycket mer fullständig information om virtuella funktioner:

Kan du skriva virtuella funktioner / metoder i Java?

Polymorfism och olika typer av övervägande

Från java tutorial

Ordbordsdefinitionen av polymorfism hänvisar till en princip i biologi där en organisme eller art kan ha många olika former eller stadier. Denna princip kan också tillämpas på objektorienterad programmering och språk som Java-språket. Underklasser i en klass kan definiera sina egna unika beteenden och ändå dela samma funktion som förälderklassen.

Titta på detta exempel för att förstå olika typer av åsidosättande.

  1. Basklass ger ingen implementering och underklassen måste åsidosätta komplett metod - (abstrakt)
  2. Basklassen ger standardimplementering och underklassen kan ändra beteendet
  3. Underklass lägger till tillägg till implementering av super.methodName() genom att kalla super.methodName() som första uttalande
  4. Basklass definierar algoritmens struktur (mallmetod) och underklassen kommer att åsidosätta en del av algoritmen

kodavsnitt:

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();
        }        
    }
}

produktion:

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow