Zoeken…


Invoering

Polymorfisme is een van de belangrijkste OOP-concepten (objectgeoriënteerd programmeren). Polymorfismewoord is afgeleid van de Griekse woorden "poly" en "morphs". Poly betekent "veel" en morphs betekent "vormen" (veel vormen).

Er zijn twee manieren om polymorfisme uit te voeren. Methode overbelast en methode overschrijft .

Opmerkingen

Interfaces zijn een andere manier om polymorfisme in Java te bereiken, afgezien van op klasse gebaseerde overerving. Interfaces definiëren een lijst met methoden die de API van het programma vormen. Klassen moeten een interface implement door alle methoden te overschrijven.

Methode Overbelasting

Methode-overbelasting , ook bekend als functie-overbelasting , is het vermogen van een klasse om meerdere methoden met dezelfde naam te hebben, gegeven dat ze verschillen in aantal of type argumenten.

Compiler controleert methode-handtekening op overbelasting van de methode.

Methode handtekening bestaat uit drie dingen -

  1. Methode naam
  2. Aantal parameters
  3. Soorten parameters

Als deze drie dezelfde zijn voor twee methoden in een klasse, geeft de compiler een dubbele methode-fout .

Dit type polymorfisme wordt statisch of compileertijd polymorfisme genoemd omdat de compiler de juiste methode wordt genoemd tijdens de compilatietijd op basis van de lijst met argumenten.

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

}

Dit zal resulteren in:

2
6
4.000000

Overbelaste methoden kunnen statisch of niet-statisch zijn. Dit heeft ook geen invloed op overbelasting van de methode.

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

Ook als u het retourtype van de methode wijzigt, kunnen we deze niet krijgen als methode-overbelasting.

public class Polymorph {  

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

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

Methode opheffen

Methode opheffen is het vermogen van subtypen om het gedrag van hun supertypen opnieuw te definiëren (opheffen).

In Java vertaalt dit zich in subklassen die de in de superklasse gedefinieerde methoden overschrijven. In Java zijn alle niet-primitieve variabelen eigenlijk references , die verwant zijn aan verwijzingen naar de locatie van het werkelijke object in het geheugen. De references slechts één type, dat is het type waarmee ze zijn gedeclareerd. Ze kunnen echter verwijzen naar een object van het opgegeven type of een van de subtypen.

Wanneer een methode op een reference wordt aangeroepen, wordt de overeenkomstige methode van het werkelijke object reference wordt reference , aangeroepen .

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

Regels om in gedachten te houden

Om een methode in de subklasse te overschrijven, MOET de vervangende methode (diegene in de subklasse) HEBBEN :

  • zelfde naam
  • hetzelfde retourtype in het geval van primitieven (een subklasse is toegestaan voor klassen, dit worden ook wel covariante retourtypen genoemd).
  • zelfde type en volgorde van parameters
  • het mag alleen die uitzonderingen werpen die worden vermeld in de werpclausule van de methode van de superklasse of uitzonderingen die subklassen zijn van de verklaarde uitzonderingen. Hij kan er ook voor kiezen GEEN uitzondering te maken. De namen van de parametertypen doen er niet toe. Void methodX (int i) is bijvoorbeeld hetzelfde als void methodX (int k)
  • We kunnen de definitieve of statische methoden niet overschrijven. Het enige dat we kunnen doen, is de methode alleen veranderen.

Gedrag toevoegen door klassen toe te voegen zonder bestaande code aan te raken

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

Uitleg

a) De klasse MakeThingsFly kan werken met alles van het type FlyingMachine .

b) De methode letTheMachinesFly werkt ook zonder enige wijziging (!) wanneer u een nieuwe klasse toevoegt, bijvoorbeeld PropellerPlane :

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

Dat is de kracht van polymorfisme. Je kunt het open-gesloten-principe ermee implementeren.

Virtuele functies

Virtuele methoden zijn methoden in Java die niet-statisch zijn en zonder het trefwoord Final ervoor. Alle methoden zijn standaard virtueel in Java. Virtuele methoden spelen een belangrijke rol in polymorfisme omdat kinderklassen op Java de methoden van hun bovenliggende klassen kunnen vervangen als de functie die wordt overschreven niet-statisch is en dezelfde methode-handtekening heeft.

Er zijn echter enkele methoden die niet virtueel zijn. Als de methode bijvoorbeeld privé wordt verklaard of met het trefwoord final, is de methode niet virtueel.

Overweeg het volgende aangepaste voorbeeld van overerving met virtuele methoden uit deze StackOverflow-post Hoe werken virtuele functies in C # en 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");

    }
}

Als we klasse B aanroepen en hello () en boo () aanroepen, krijgen we "Nee" en "Zeg haha" als de resulterende uitvoer omdat B dezelfde methoden uit A overschrijft, hoewel het bovenstaande voorbeeld bijna exact hetzelfde is als methode negeert, is het belangrijk om te begrijpen dat de methoden in klasse A standaard allemaal virtueel zijn.

Bovendien kunnen we virtuele methoden implementeren met behulp van het abstracte trefwoord. Methoden die worden verklaard met het trefwoord "abstract" hebben geen methodedefinitie, wat betekent dat de body van de methode nog niet is geïmplementeerd. Beschouw het voorbeeld van hierboven opnieuw, behalve dat de methode boo () abstract wordt verklaard:

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

    }
}

Als we boo () uit B aanroepen, is de uitvoer nog steeds "Say haha", omdat B de abstracte methode boo () overneemt en boo () de uitvoer "Say haha" maakt.

Gebruikte bronnen en verdere metingen:

Hoe werken virtuele functies in C # en Java?

Bekijk dit geweldige antwoord dat veel completere informatie geeft over virtuele functies:

Kun je virtuele functies / methoden schrijven in Java?

Polymorfisme en verschillende soorten override

Van Java- tutorial

De woordenboekdefinitie van polymorfisme verwijst naar een biologieprincipe waarin een organisme of soort veel verschillende vormen of stadia kan hebben. Dit principe kan ook worden toegepast op objectgeoriënteerd programmeren en talen zoals de Java-taal. Subklassen van een klasse kunnen hun eigen unieke gedrag definiëren en toch dezelfde functionaliteit van de bovenliggende klasse delen.

Bekijk dit voorbeeld voor meer informatie over de verschillende soorten overschrijvingen.

  1. Basisklasse biedt geen implementatie en subklasse moet de volledige methode overschrijven - (abstract)
  2. Basisklasse biedt standaardimplementatie en subklasse kan het gedrag veranderen
  3. super.methodName() voegt uitbreiding toe aan basisklasse-implementatie door super.methodName() aan te roepen als eerste statement
  4. Basisklasse definieert de structuur van het algoritme (sjabloonmethode) en subklasse zal een deel van het algoritme overschrijven

codefragment:

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

output:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow