수색…


소개

다형성 (Polymorphism)은 주요 OOP (객체 지향 프로그래밍) 개념 중 하나입니다. 다형성 단어는 "poly"와 "morphs"라는 그리스 단어에서 파생되었습니다. Poly는 "many"를 의미하고 morph는 "forms"(여러 형태)를 의미합니다.

다형성을 수행하는 두 가지 방법이 있습니다. 메소드 오버로딩메소드 오버라이드 .

비고

Interfaces 는 클래스 기반 상속과는 별도로 Java에서 다형성을 구현하는 또 다른 방법입니다. 인터페이스는 프로그램의 API를 구성하는 메소드 목록을 정의합니다. 클래스는 모든 메소드를 오버라이드하여 interfaceimplement 해야합니다.

메서드 오버로딩

메서드 오버로드함수 오버로드 라고도하며 클래스의 수 또는 형식이 서로 다른 동일한 이름을 가진 여러 메서드를 갖는 클래스의 기능입니다.

컴파일러는 메서드 오버로드에 대한 메서드 서명 을 확인합니다.

메소드 서명은 세 가지로 구성됩니다.

  1. 메서드 이름
  2. 매개 변수의 수
  3. 매개 변수의 유형

이 세가 클래스의 두 메소드에서 동일하면 컴파일러가 중복 메소드 오류를 발생시킵니다 .

이러한 유형의 다형성은 정적 또는 컴파일 시간 다형성이라고 부릅니다. 호출 할 적절한 메소드가 인수 목록을 기반으로 컴파일 시간 동안 컴파일러에 의해 결정되기 때문입니다.

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

}

결과는 다음과 같습니다.

2
6
4.000000

오버로딩 된 메서드는 정적 또는 비 정적 일 수 있습니다. 이것은 메소드 오버로딩에도 영향을 미치지 않습니다.

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

또한 메서드의 반환 형식을 변경하는 경우 메서드 오버로드로 가져올 수 없습니다.

public class Polymorph {  

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

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

메서드 재정의

메소드 겹쳐 쓰기는 부속 유형의 수퍼 유형의 작동을 다시 정의 (재 지정)하는 부속 유형의 기능입니다.

자바에서 이것은 수퍼 클래스에 정의 된 메소드를 오버라이드하는 서브 클래스로 변환된다. 자바에서 모든 프리미티브가 아닌 변수는 실제로 메모리에있는 실제 객체의 위치를 ​​가리키는 포인터와 유사한 references 입니다. references 에는 하나의 유형 만 있으며이 유형은 선언 된 유형입니다. 그러나 선언 된 유형 또는 해당 부속 유형의 오브젝트를 가리킬 수 있습니다.

reference 에서 메소드가 호출되면, 가리키는 실제 오브젝트 의 해당 메소드가 호출됩니다 .

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

명심해야 할 규칙

서브 클래스의 메소드를 오버라이드 (override)하려면, 오버라이드 (override) 메소드 (서브 클래스의 메소드)가 반드시 필요합니다 .

  • 같은 이름
  • 원시 타입의 경우에는 동일한 반환 유형 (클래스에 대해 하위 클래스가 허용되며, 이는 공변 반환 유형이라고도 함).
  • 동일한 유형 및 매개 변수 순서
  • 수퍼 클래스의 메소드의 throws 절에서 선언 된 예외 또는 선언 된 예외의 서브 클래스 인 예외 만 예외로 throw 할 수 있습니다. 예외를 throw하지 않기로 선택할 수도 있습니다. 매개 변수 유형의 이름은 중요하지 않습니다. 예를 들어, void methodX (int i)는 void methodX (int k)와 동일하다.
  • 최종 또는 정적 메서드를 재정의 할 수 없습니다. 우리가 할 수있는 것만이 방법 몸만 바꿉니다.

기존 코드를 건드리지 않고 클래스를 추가하여 비헤이비어 추가하기

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

설명

a) MakeThingsFly 클래스는 FlyingMachine 타입의 모든 것을 처리 할 수 ​​있습니다.

b) letTheMachinesFly 메소드는 새 클래스를 추가 할 때 (예 : PropellerPlane ) 아무런 변화없이 작동합니다 (!).

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

그것은 다형성의 힘입니다. 이를 사용하여 개방형 폐쇄 원리 를 구현할 수 있습니다.

가상 함수

버추얼 메소드는 비 정적이며 키워드 Final이없는 ​​Java의 메소드입니다. 기본적으로 모든 메소드는 Java에서 가상입니다. 가상 메소드는 다형성에서 중요한 역할을합니다. 왜냐하면 재정의되는 함수가 비 정적이고 메소드 서명이 동일한 경우 자바의 하위 클래스가 상위 클래스의 메소드를 대체 할 수 있기 때문입니다.

그러나 가상이 아닌 몇 가지 방법이 있습니다. 예를 들어, 메서드가 private 또는 final 키워드로 선언 된 경우 메서드는 가상이 아닙니다.

이 StackOverflow 포스트에서 Virtual Methods를 사용하여 상속을 수정 한 다음 예제를 고려하십시오. C # 및 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");

    }
}

클래스 B를 호출하고 hello () 및 boo ()를 호출하면 B가 A와 동일한 메서드를 재정의하므로 "No"와 "Say haha"를 결과로 얻습니다. 위 예제는 메서드 재정의를 위해서는 클래스 A의 메서드가 기본적으로 모두 가상이라는 것을 이해하는 것이 중요합니다.

또한 abstract 키워드를 사용하여 가상 메소드를 구현할 수 있습니다. 키워드 "abstract"로 선언 된 메소드에는 메소드 정의가 없으므로 메소드의 본문이 아직 구현되지 않았습니다. boo () 메서드가 abstract로 선언 된 것을 제외하고는 위의 예제를 다시 고려하십시오.

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

    }
}

B에서 boo ()를 호출하면 B는 추상 메소드 boo ()를 상속하고 boo () 출력에 "Say haha"를 출력하므로 "Say haha"가 출력됩니다.

사용 된 소스 및 추가 읽기 :

C # 및 Java에서 가상 함수는 어떻게 작동합니까?

가상 기능에 대한 훨씬 더 완벽한 정보를 제공하는 훌륭한 대답을 확인하십시오.

자바에서 가상 함수 / 메소드를 작성할 수 있습니까?

다형성 및 다른 유형의 재정의

자바 자습서에서

다형성에 대한 사전 정의는 생물 또는 종이 여러 가지 형태 또는 단계를 가질 수있는 생물학의 원리를 나타냅니다. 이 원칙은 객체 지향 프로그래밍 및 Java 언어와 같은 언어에도 적용될 수 있습니다. 클래스의 서브 클래스는 고유 한 비헤이비어를 정의 할 수 있지만 부모 클래스와 동일한 기능을 일부 공유 할 수 있습니다.

다른 우선 순위 유형을 이해하려면이 예제를 살펴보십시오.

  1. 기본 클래스는 구현을 제공하지 않으며 하위 클래스는 완전한 메소드를 오버라이드해야합니다 - (추상)
  2. 기본 클래스는 기본 구현을 제공하고 하위 클래스는 동작을 변경할 수 있습니다.
  3. 하위 클래스는 super.methodName() 을 첫 번째 문으로 호출하여 기본 클래스 구현에 확장을 추가합니다.
  4. 기본 클래스는 알고리즘 (템플릿 메소드)의 구조를 정의하고 하위 클래스는 알고리즘의 일부를 오버라이드합니다

코드 스 니펫 :

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

산출:

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
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow