Java Language
Polymorphismus
Suche…
Einführung
Polymorphismus ist eines der wichtigsten OOP-Konzepte (objektorientierte Programmierung). Das Polymorphismuswort wurde von den griechischen Wörtern "Poly" und "Morphen" abgeleitet. Poly bedeutet "viele" und Morphen bedeutet "Formen" (viele Formen).
Es gibt zwei Möglichkeiten, Polymorphie durchzuführen. Method Overloading und Method Overriding .
Bemerkungen
Interfaces
sind ein weiterer Weg, um Polymorphismus in Java zu erreichen, abgesehen von der Klassenvererbung. Schnittstellen definieren eine Liste von Methoden, die die API des Programms bilden. Klassen müssen eine interface
implement
interface
indem sie alle ihre Methoden überschreiben.
Methodenüberladung
Methodenüberladung , auch Funktionsüberladung genannt , ist die Fähigkeit einer Klasse, mehrere Methoden mit demselben Namen zu haben, vorausgesetzt, sie unterscheiden sich entweder in der Anzahl oder im Typ der Argumente.
Der Compiler überprüft die Methodensignatur auf Methodenüberladung.
Die Methodensignatur besteht aus drei Dingen:
- Methodenname
- Anzahl der Parameter
- Arten von Parametern
Wenn diese drei für zwei Methoden in einer Klasse gleich sind, gibt der Compiler einen doppelten Methodenfehler aus .
Diese Art von Polymorphismus wird als statischer Polymorphismus oder als Kompilierzeitpolymorphismus bezeichnet, da die geeignete aufzurufende Methode vom Compiler während der Kompilierzeit auf Grundlage der Argumentliste festgelegt wird.
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));
}
}
Dies führt zu:
2
6
4.000000
Überladene Methoden können statisch oder nicht statisch sein. Dies wirkt sich auch nicht auf das Überladen von Methoden aus.
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
}
}
Wenn Sie den Rückgabetyp der Methode ändern, können wir sie nicht als Methodenüberladung abrufen.
public class Polymorph {
void methodOverloaded(){
//No argument and No return type
}
int methodOverloaded(){
//No argument and int return type
return 0;
}
Überschreiben der Methode
Das Überschreiben von Methoden ist die Fähigkeit von Subtypen, das Verhalten ihrer Supertypen neu zu definieren (zu überschreiben).
In Java wird dies in Unterklassen übersetzt, die die in der Oberklasse definierten Methoden überschreiben. In Java sind alle nicht primitiven Variablen tatsächlich references
, die mit Zeigern auf die Position des tatsächlichen Objekts im Arbeitsspeicher verwandt sind. Die references
nur einen Typ, den Typ, mit dem sie deklariert wurden. Sie können jedoch auf ein Objekt des deklarierten Typs oder eines seiner Subtypen zeigen.
Wenn eine Methode für eine reference
aufgerufen wird, wird die entsprechende Methode des tatsächlichen Objekts aufgerufen, auf das gezeigt wird .
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
}
}
Regeln, die zu beachten sind
Um eine Methode in der Unterklasse zu überschreiben, MUSS die überschreibende Methode (dh die in der Unterklasse) HABEN :
- gleicher Name
- gleicher Rückgabetyp bei Primitiven (eine Unterklasse ist für Klassen zulässig, dies wird auch als kovariante Rückgabetypen bezeichnet)
- gleicher Typ und Reihenfolge der Parameter
- Es kann nur die Ausnahmen werfen, die in der Throws-Klausel der Methode der Superklasse deklariert sind, oder Ausnahmen, die Unterklassen der deklarierten Ausnahmen sind. Es kann sich auch entscheiden, KEINE Ausnahme auszulösen. Die Namen der Parametertypen spielen keine Rolle. Zum Beispiel ist void methodX (int i) mit void methodX (int k) identisch.
- Endgültige oder statische Methoden können nicht überschrieben werden. Nur, dass wir nur den Methodenkörper ändern können.
Hinzufügen von Verhalten durch Hinzufügen von Klassen, ohne vorhandenen Code zu berühren
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");
}
}
Erläuterung
a) Die MakeThingsFly
Klasse kann mit FlyingMachine
Typen vom Typ FlyingMachine
.
b) Die Methode letTheMachinesFly
funktioniert auch ohne Änderung (!), wenn Sie eine neue Klasse hinzufügen, beispielsweise PropellerPlane
:
public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
for (FlyingMachine flyingMachine : flyingMachines) {
flyingMachine.fly();
}
}
}
Das ist die Kraft des Polymorphismus. Sie können das Open-Closed-Prinzip damit implementieren.
Virtuelle Funktionen
Virtuelle Methoden sind Methoden in Java, die nicht statisch sind und das Schlüsselwort Final nicht enthalten. Alle Methoden sind standardmäßig in Java virtuell. Virtuelle Methoden spielen eine wichtige Rolle in Polymorphism, da untergeordnete Klassen in Java die Methoden ihrer übergeordneten Klassen überschreiben können, wenn die zu überschreibende Funktion nicht statisch ist und dieselbe Methodensignatur aufweist.
Es gibt jedoch einige Methoden, die nicht virtuell sind. Wenn die Methode beispielsweise als privat oder mit dem Schlüsselwort final deklariert ist, ist die Methode nicht virtuell.
Betrachten Sie das folgende modifizierte Beispiel für die Vererbung mit virtuellen Methoden aus diesem StackOverflow-Beitrag. Wie funktionieren virtuelle Funktionen in C # und 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");
}
}
Wenn wir die Klasse B aufrufen und hello () und boo () aufrufen, erhalten wir als Ergebnis die Ausgabe "No" und "Say haha", da B die gleichen Methoden von A überschreibt Wenn Sie die Methode überschreiben, ist es wichtig zu verstehen, dass die Methoden in Klasse A standardmäßig alle Virtual sind.
Außerdem können wir virtuelle Methoden mit dem abstrakten Schlüsselwort implementieren. Methoden, die mit dem Schlüsselwort "abstract" deklariert werden, haben keine Methodendefinition, dh der Körper der Methode ist noch nicht implementiert. Betrachten Sie das Beispiel von oben noch einmal, außer die boo () -Methode ist als abstrakt deklariert:
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");
}
}
Wenn wir boo () von B aufrufen, ist die Ausgabe immer noch "Say haha", da B die abstrakte Methode boo () erbt und boo () die Ausgabe "Say haha" ausgibt.
Verwendete Quellen und weitere Lesungen:
Wie funktionieren virtuelle Funktionen in C # und Java?
Sehen Sie sich diese großartige Antwort an, die umfassendere Informationen zu virtuellen Funktionen enthält:
Können Sie virtuelle Funktionen / Methoden in Java schreiben?
Polymorphismus und verschiedene Arten des Überschreibens
Aus dem Java- Tutorial
Die Wörterbuchdefinition für Polymorphismus bezieht sich auf ein Prinzip in der Biologie, bei dem ein Organismus oder eine Spezies viele verschiedene Formen oder Stufen haben kann. Dieses Prinzip kann auch auf objektorientierte Programmierung und Sprachen wie die Java-Sprache angewendet werden. Unterklassen einer Klasse können ihre eigenen eindeutigen Verhalten definieren und dabei einige Funktionen der übergeordneten Klasse gemeinsam nutzen.
Sehen Sie sich dieses Beispiel an, um die verschiedenen Arten des Überschreibens zu verstehen.
- Die Basisklasse stellt keine Implementierung bereit und die Unterklasse muss die vollständige Methode überschreiben. (Abstrakt)
- Die Basisklasse stellt eine Standardimplementierung bereit und eine Unterklasse kann das Verhalten ändern
- Die Unterklasse fügt der Basisklassenimplementierung eine Erweiterung hinzu, indem sie
super.methodName()
als erste Anweisungsuper.methodName()
- Die Basisklasse definiert die Struktur des Algorithmus (Template-Methode) und die Unterklasse überschreibt einen Teil des Algorithmus
Code-Auszug:
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();
}
}
}
Ausgabe:
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: