열렬히.뛰기

다형성

Language > 프로그래밍 언어 > Java > 다형성

다형성이란?

사전적으로는 여러 가지 형태를 가질 수 있는 능력을 의미함.

또, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것.

다형성의 예시

기본적으로 생성된 객체를 다루기 위해선, 객체의 타입과 일치하는 참조변수를 사용.

java
Tv t = new Tv();
SamsungTv sam = new SamsungTv();

그러나 만약 두 클래스가 상속관계에 있다면, 다음과 같이 작성할 수도 있다.

java
Tv t = new SamsungTv();

이는 SamsungTv 클래스가 Tv 클래스의 자손클래스이기 때문에 가능한 것이다.

이때 사용할 수 있는 메소드는 다음과 같다.

  • SamsungTv 클래스의 메소드 중 Tv 클래스에 정의되어 있는 것.

반대의 경우는 어떨까?

java
SamsungTv sam = new Tv();

이 경우에는 아예 컴파일 자체가 되지 않는다.

다형성의 규칙

위와 같은 예시를 볼 때…

  1. 조상타입의 참조변수로 자손타입의 인스턴스는 참조 가능하다.
  2. 반대는 되지 않는다.

참조변수의 형변환

참조변수끼리도 형변환이 가능하다.

예를 들어, 다음과 같은 클래스 간 구조가 잡혀있다고 보자.

mermaid
classDiagram
	Car <|-- sedan
	Car <|-- suv
	class Car
	class sedan
	class suv

이 상태에서 형변환이 가능한 것을 보자.

java
// 불가능한 예시
Sedan s1;
Suv   s2;
s1 = (suv) s2;

// 가능한 예시
Car car = null;
Sedan s3 = new Sedan();
Sedan s4 = null;

car = s4;        // 업캐스팅
s3 = (Sedan)car; // 다운캐스팅

자손타입의 참조변수를 조상타입에 할당 = 업캐스팅

  • 이 경우에는 형변환을 생략 가능하다.

조상타입의 참조변수를 자손타입에 할당 = 다운캐스팅

  • 이 경우에는 형변환을 꼭 명시해야 한다.

instanceof 연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알기 위해 사용.

주로 다음과 같이 if문과 함께 사용한다.

java
void ft_work(Car c) {
	if (c instanceof Sedan) {
		Sedan sd = (Sedan)c;
		sd.audio();
	}
	else
		Suv suv = (Suv)c;
		suv.wheel();
}

만약, c가 생성되는 과정에서 Sedan 인스턴스를 사용했다면 위의 경우로,

아니라면 아래의 경우로 넘어갈 것이다.

참조변수와 인스턴스의 연결

만약 조상클래스와 자손클래스에 같은 메소드와 같은 값이 있는 상황에서,

참조변수의 형변환이 일어나면 어떻게 될까?

java
class Parent {
	int x = 100;
	void method() {
		System.out.println("Parent");
	}
}

class Child extends Parent {
	int x = 200;
	void method() {
		System.out.printlnt("child");
	}
}

class test {
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();
		
		// 메소드는 child를 따른다.
		p.method();
		c.method();
		
		// 멤버변수는 각각 100, 200으로 나온다.
		System.out.printf("%d %d", p.x, c.x);
	}
}

이 경우 메소드와 멤버변수에 따라 달라진다.

  • 메소드는 자식클래스의 것을 따른다.
  • 멤버변수는 각 클래스의 값을 따른다.

매개변수의 다형성

참조변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.

예를 들어, print 메소드의 경우…

java
public void print(Object obj) {
	write(String.valueOf(obj);
}

public static String valueOf(Object obj) {
	return (obj == null) ? "null" : obj.toString();
}

print 메소드는 모든 타입을 출력해야 한다.

그런데 타입마다 어떻게 출력할지에 대해서 적는 것은 너무 비효율적이다.

따라서, 모든 타입의 상위클래스인 Object타입을 이용한다.

즉, 상위클래스 Object의 참조변수 obj로 하위클래스의 인스턴스를 처리할 수 있게 되며 다형성을 이용한 사례라고 할 수 있다.

객체를 담는 배열

다음과 같은 클래스의 관계가 있다고 하자.

mermaid
classDiagram
	Car <|-- sedan
	Car <|-- suv
	Car <|-- sports
	class Car
	class sedan
	class suv
	class sports

그럼 배열을 이용해 다음의 case도 만들 수 있다.

java
Car cars[] = new cars[3];
cars[0] = new Sedan();
cars[1] = new Suv();
cars[2] = new Sports();