다형성 - Polymorphism

minjjai·2022년 11월 18일
0

다형성

  • 여러 가지 형태를 가질 수 있는 능력
  • 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 하는 것
    -> 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것
class Parent {
	void a() {}
    void b() {}
}
class Child extends Parent {
	void c() {}
}

public class Main {
	public static void main(String[] args) {
    		Parent p = new Child();
            Child c = new Child();
	}
}

위의 코드를 보면 Child클래스가 Parent클래스를 상속받고 있다.
앞서 설명한 것처럼 조상클래스 Parent타입인 참조변수 p로 자손클래스 Child인스턴스를 참조할 수 있다.

반대로 자손클래스 타입의 참조변수로 조상클래스의 인스턴스를 참조하는 것은 불가능하다.

  • 참조변수 p가 가리키는 객체는 조상이 가진 a, b메서드만 호출 가능하다.
  • 참조변수 c가 가리키는 객체는 a, b, c메서드를 모두 호출 가능하다.

조상클래스 타입의 참조변수로 자손클래스 인스턴스를 참조했을 경우에, 자손 인스턴스의 멤버는 사용할 수 없고, 조상클래스에 있는 멤버만 사용가능하다.

위의 참조변수 p와 c가 참조하는 인스턴스는 Child로 같지만 참조변수(리모콘)의 타입에 따라서 사용할 수 있는 멤버에는 차이가 있다.

참조변수의 형변환

  • 사용할 수 있는 멤버의 개수를 조절하는 것
    상속관계에 있는 클래스들은 서로 형변환이 가능하다.

기본형인 int와 long을 예로 들어 설명하면,
int에서 long으로 형변환을 하는 것은 값의 손실이 발생하지 않기 때문에, 형변환을 생략할 수 있다.
반대로 long에서 int로 형변환을 하려면, long타입의 값의 범위가 더 크기 때문에 형변환을 명시해주어야 한다.

참조변수의 형변환도 이와 마찬가지이다.
자손타입에서 조상타입으로 형변환을 하는 경우에는 자손타입의 인스턴스가 가진 멤버가 더 많거나 같기 때문에, 형변환을 생략할 수 있다.
반대로 조상타입에서 자손타입으로 형변환을 하는 경우에는 형변환을 명시해 주어야 한다.

자손타입 -> 조상타입 : 형변환 생략가능
조상타입 -> 자손타입 : 형변환 생략불가

instanceOf

  • 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위함.
  • 형변환 전에 형변환의 가능여부를 판단하기 위해
  • 주로 조건문(if)에 사용된다.
  • instanceOf연산자의 왼쪽에는 참조변수가 위치하고, 오른쪽에는 타입이 위치한다.
  • 연산의 결과는 boolean타입으로 반환된다.
  • 만약 연산의 결과가 true라면 참조변수가 해당 타입으로 형변환이 가능하다는 것을 의미한다.

다형성의 장점

  • 여러 객체를 하나의 타입으로 관리가 가능하기 때문에 코드 관리가 편해져 유지보수가 용이해진다.
  • 객체를 재사용하기가 용이해져 코드 재사용성이 증가한다.
  • 확장성이 좋은 코드를 작성할 수 있고, 결합도가 강하지 않은 코드를 작성할 수 있다.

매개변수의 다형성

다형성의 장점 중 하나는 매개변수의 형태로 여러객체를 취할 수 있다는 것이다.
아래의 코드를 보자

class Product {
	int price;
	int bonusPoint;
	
	Product(int price) {
		this.price = price;
		bonusPoint = (int)(price / 10.0);
	}
}
class Tv extends Product {
	Tv() {
		super(100);
	}
	public String toString() { return "Tv"; }
}
class Computer extends Product {
	Computer() { super(250); }
	public String toString() { return "Computer"; }
}
class Audio extends Product {
	Audio() { super(50); }
    public String toString() { return "Audio"; }
}
class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	
	void buy(Product p) { //Product의 자손 객체가 들어갈 수 있으므로 객체 각각에 대해서 메서드를 만들 필요가 없다.
		if(money < p.price) {
			System.out.println("잔액이 부족합니다");
			return;
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		System.out.println(p + " 구입완료");
	}
}

Tv, Computer, Audio클래스는 각각 Product라는 하나의 클래스를 상속받고 있다.
Buyer클래스의 buy메서드의 매개변수가 Product타입이다.
이것은 곧 product클래스를 상속받는 클래스들이 모두 buy메서드의 매개변수로 사용될 수 있다는 것을 의미한다.
이렇게 다형성을 이용함으로써, Tv, Computer, Audio클래스 각각에 대해서 buy메서드를 만들지 않아도 되는 것이다. -> 코드 중복 감소, 코드 재사용

  • Product의 자손클래스임으로 아래와 같이 Product타입의 참조변수로 Tv, Computer, Audio인스턴스를 참조할 수 있다.
Product p1 = new Tv();
Product p2 = new Computer();
Product p3 = new Audio();

하나의 배열로 여러 종류의 객체를 다룰 수 있다.

int[] array = new int[];

위와 같이 int타입의 배열을 생성하면 그 배열의 요소로는 int타입만 들어갈 수 있다.
하지만 객체지향의 다형성을 활용하면 특정 타입의 배열의 요소에 여러가지 타입의 객체를 넣을 수 있다.
위의 Product, Tv, Computer, Audio클래스를 보자
Tv, Computer, Audio클래스는 Product를 상속받는다.

Product[] p = new Product[2];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();

위와 같이 Tv, Computer, Audio가 공통으로 상속받는 Product타입의 배열을 생성하면 Product클래스를 상속받는 Tv, Computer, Audio타입의 객체가 해당 배열의 요소로 들어갈 수 있다.

profile
BackEnd Developer

0개의 댓글