Language/Java

[Java] 다형성

by Donghwan 2021. 1. 1.

다형성이란 하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작하는 것을 의미합니다.

예를 들어 키보드라는 클래스에 누른다라는 동작이 있습니다. 그렇다면 ENTER를 누르는 것과 ESC, SPACE를 누르는 것은 서로 다른 동작을 하게 됩니다. 이러한 동작을 다형성이라고 합니다.

다형성에 대해 가장 많이 언급되는 것이 상속과 Overriding, OverLoading입니다. OverLoading에 대해서는 의견이 분분하다고 많은 책들에서 언급되지만 예시에 포함하도록 하겠습니다.

 

Overloading

같은 이름을 가지지만 매개변수의 개수 또는 타입에 따라 서로 다른 동작 방법을 하는 것을 오버로딩이라고 합니다.

public class Printer {
    public void print(int number) {
        System.out.println("Number Print");
    }

    public void print(String text) {
        System.out.println("Text Print");
    }
}

public class Main {
    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print(1);
        printer.print("Text");
    }
}

//실행결과
//Number Print
//Text Print

Printer라는 클래스가 존재하고 내부에 print라는 메서드를 정의할 때, 이름과 매개변수의 수는 동일하지만 각기 다른 동작을 하게 됩니다.

오버로딩은 다음과 같은 조건을 만족해야 합니다.

  • 메서드의 이름이 같아야 합니다.
  • 매개 변수의 개수 또는 타입이 달라야 합니다.
  • 매개 변수는 같고 리턴 타입이 다른 경우는 오버로딩이 성립되지 않습니다.
  • 오버로딩된 메서드들은 매개 변수에 의해서만 구별될 수 있습니다.

 

Overriding

오버라이딩은 상위(슈퍼) 클래스의 메서드를 하위(서브) 클래스에서 재정의하여 사용하는 것을 말합니다.

public class Printer {
    public void print() {
        System.out.println("Print");
    }
}

public class RazorPrinter extends Printer {
    public void print() {
        System.out.println("Razor Print");
    }
}

public class Main {
    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
        RazorPrinter razorPrinter = new RazorPrinter();
        razorPrinter.print();
    }
}

//실행결과
//Print
//Razor Print

하지만 오버라이딩에도 조건이 존재합니다. 아래와 같은 조건이 성립하여야 오버라이딩을 할 수 있습니다.

  • 메소드의 이름
  • 메소드 매개변수의 숫자와 데이터 타입 그리고 순서
  • 메소드의 리턴 타입

만약 리턴타입이 다를 경우 attempting to use incompatible return type라는 에러가 발생합니다.

 

상속

상속은 우리가 알고 있는 의미로 물려 받는다는 개념입니다. B라는 클래스가 A라는 클래스를 상속 받게 된다면 B 클래스는 A 클래스의 멤버 변수와 메서드를 사용할 수 있습니다.

객체 지향 프로그램은 유지 보수하기 편하고 기능의 수정 또는 추가가 유연하도록 해주는 장점이 있습니다. extends를 예약어를 통해 상속이 가능하며 자바는 단일 상속만 가능합니다.

public class Customer {
    protected String name;

    public void getInformation() {
        System.out.println(name + "은 " + this.getClass().getSimpleName() + "입니다.");
    }

    public Customer(String name) {
        this.name = name;
    }
}

public class GoldCustomer extends Customer{
		private int number = 0;

    public void testMethod() {
        System.out.println("Test");
    }

    public GoldCustomer(String name) {
        super(name);
    }
}

public class SilverCustomer extends Customer{
    public SilverCustomer(String name) {
        super(name);
    }
}

public class BronzeCustomer extends Customer{
    public BronzeCustomer(String name) {
        super(name);
    }
}

public class Main {
    public static void main(String[] args) {
        GoldCustomer customer1 = new GoldCustomer("김동환");
        customer1.getInformation();
        customer1.testMethod();
        Customer customer1_1 = new GoldCustomer("김");
        customer1_1.getInformation();
        //customer1_1.testMethod();
        //error - Cannot resolve method 'testMethod' in 'Customer'
        Customer customer2 = new SilverCustomer("동");
        customer2.getInformation();
        Customer customer3 = new BronzeCustomer("환");
        customer3.getInformation();
    }
}

//실행결과
//김동환은 GoldCustomer입니다.
//김은 GoldCustomer입니다.
//동은 SilverCustomer입니다.
//환은 BronzeCustomer입니다.

Customer의 getInformation() 메서드를 정의하고 상속받은 Gold,Silver,Bronze가 getInformation() 메서드를 호출 했을 때, 각각의 객체에서 호출이 가능한 것을 볼 수 있습니다.

Customer 클래스를 상속받은 GoldCustomer 클래스에 특정 멤버 변수 또는 메서드를 정의하여 사용할 수 있고 상위 클래스에 존재하는 함수를 오버라이딩하여 사용할 수 있습니다. 상속을 받아서 사용할 때, 주의할 점은 접근제어자가 private, default로 선언된 변수는 하위 클래스에서 사용할 수 없습니다. (public, protected만 가능합니다.)

또 각 클래스의 생성자에서 super() 메서드를 통해 상위 클래스에 매개변수를 전달하여 Customer에 존재하는 name에 값을 할당 할 수 있습니다. super() 메서드는 this처럼 주소를 가지고 있습니다. 이때 super()가 가지고 있는 주소는 상위 클래스의 주소입니다. super를 통해 상위 클래스의 변수 또는 메서드에 접근이 가능합니다.

위 코드에서 특이한 점은 customer1과 customer1_1은 같은 GoldCustomer를 인스턴스를 생성했지만 customer1은 testMethod()를 실행할 수 있고, customer1_1은 실행할 수 없습니다. 이유는 GoldCustomer는 Customer를 상속 받기 때문에 GoldCustomer이면서 Customer입니다. 따라서 GoldCustomer는 Customer로 캐스팅되어 사용할 수 있습니다. 하지만 역으로 Customer는 GoldCustomer가 될 수 없기때문에 선언된 클래스형에 따라 특정 메서드를 사용할 수 없습니다. 위에 문제를 해결하기 위해서는 instanceof 예약어를 사용하여 GoldCustomer로 형변환을 해준다면 testMethod()를 사용할 수 있습니다.

위의 내용을 자바의 메모리적 관점에서 설명하자면 하위 클래스(GoldCustomer)가 생성될 때, 상위 클래스(Customer)의 생성자가 먼저 호출되어 자바의 힙 메모리 공간 상위 클래스(Customer)의 인스턴스가 위치하게 되고 그 다음 하위 클래스의 생성자가 호출되어 힙 메모리 공간에 하위 클래스(GoldCustomer)의 인스턴스가 위치하게 됩니다. 따라서 하위 클래스는 상위 클래스를 메모리 공간에 가지고 있기 때문에 상위 클래스이기도 하다고 볼 수 있습니다.

728x90
반응형

'Language > Java' 카테고리의 다른 글

[Java] 객체 지향 언어  (0) 2021.09.29
[Java] 제네릭 (Generic)  (0) 2021.04.21
[Java] 실행 데이터 영역 (Runtime Data Area)  (0) 2020.12.27
[Java] 자바 클래스 로더  (0) 2020.12.26
[Java] 자바란? ( Java )  (0) 2020.12.25

댓글