본문 바로가기
  • 비둘기다
  • 비둘기다
  • 비둘기다
코딩/JAVA Basics

[자바 JAVA] 다형성

by parzival56 2022. 12. 5.

다형성이란 여러 부분에서 정의가 내려집니다. 생물학적인 정의를 따르면 같은 종이면서도 다른 형질을 띠는 현상이라고 합니다. 예를 들면, 암수에 따라 크기가 다른 생물이라던지, 갈기의 유무가 있는 사자라던지 등이 되겠네요.

자바에서 다형성을 사용하는 이유는 하나의 기능으로 여러 가지 명령을 수행하기 위함입니다.

 

다형성은 자바를 하시면서 겪어보셨을 데이터 타입 변환과 유사한 개념입니다. 저희는 데이터 타입 변환을 할 때 자동 형 변환과 강제 형 변환을 알아봤었는데 이 다형성도 이와 마찬가지로 객체의 타입을 변환하는 행위입니다. 

 

객체의 타입을 변환한다라...먼저 클래스란 객체 지향 프로그래밍에서 데이터와 조작 가능한 메서드를 담고 있는 객체의 추형 즉, 형태입니다. 예를 들어 피보나치수열을 계산해주는 클래스 fibo를 만들었다고 했을 때, 마냥 class fibo를 생성했다고 해서 이를 써먹을 수 있는 것이 아닙니다. (static 머시기를 사용하지 않는 이상..) 클래스는 그저 형태일 뿐 이 형태가 하는 일을 직업으로 삼아줄 사람 즉, 객체가 있어야 클래스 내의 데이터들이 정상적으로 일을 할 수 있게 되는 것입니다.

그러나 객체의 타입을 변환한다고 했습니다. 객체를 생성할 때는 보통 fibo f = new fibo();처럼 생성하죠.

즉, 타입이 일반적인 데이터처럼 int, long, float, double, String 등이 아니라 클래스의 이름이 됩니다.

그러면 적어도 다른 클래스가 있어야 변환이 가능할텐데 그냥 생성한다고 해서 변환이 될까요? 아무 관련도 없는데..?

 

그래서 다형성은 필수적으로 상속관계인 클래스끼리만 가능한 기능입니다. 상속관계라는 것은 즉, 부모를 매개로 하여 모든 것이 연결이 되기 때문에 기초적인 기능은 모두 비슷하다는 것을 의미합니다. 그래서 이들끼리는 타입 변환이 가능합니다. 

다형성을 사용하는 대다수의 경우는 부모로부터 파생된 수많은 자식들을 한꺼번에 관리하기 위해서입니다. 예를 들어 

Person이라는 부모클래스 밑에 Doctor, Police, Student, Soldier 등이 있다고 했을 때, 이 4가지 자식 클래스는 이들의 공통된 특징인 '사람'이라는 것으로 한데 묶을 수 있습니다. 그렇기에 예를 들어 사람을 한꺼번에 동물도 따로 한꺼번에 다루려고 할 때 이 자식 클래스들을 상위의 클래스인 Person으로 묶으면 동시에 명령 처리가 가능하다는 것입니다. 

 

다형성의 객체 타입변환도 데이터 타입 변환과 마찬가지로 자동 타입 변환과 강제 타입 변환이 있습니다. 

 

자동 타입변환

예를 들어 Person의 자식 클래스 Student가 있다고 할 때

Student s = new Student();
Person p = s;

 라고 한다면 자식 클래스인 Student의 객체인 s가 상위 클래스인 Person으로 자동 타입 변환이 됩니다.

p = s;를 풀어서 써보면 p = (Person) s;와 같습니다. 이는 거의 데이터 타입 변환과 유사합니다.

대신 자동 타입 변환은 상속 계층의 상위이면 가능합니다. 

강제 타입변환

다음은 강제 타입 변환입니다. 강제 타입이 필요한 경우로는 크게 부모 타입을 자식 타입으로 변환할 때입니다. 위의 자동 변환 같은 경우에는 대부분 상위 계층으로 변환하는 경우에만 사용했습니다. 왜냐하면 반대 방향으로 자동 타입 변환을 할 경우에는 부모에는 없는 메서드가 자식에게는 있을 시에 충돌이 생기기 때문입니다. 

그래서 이러한 고충으로 강제 타입변환을 생각해봤을 때

Person p = new Person();
Student s = (Student)p;
// 이렇게 강제 변환하면 오류...

Student s1 = new Student();
Person p = s1;
Student s2 = (Student) p;
// 이렇게 되면 부모타입의 변수이긴 하지만 자식 객체를 가리킨다

이렇게 부모 타입을 자식 타입으로 변환할 때 조건은 자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때입니다. 여기서 상속관계상 위로 타입변환을 이루는 것을 업 캐스팅, 밑으로 변환하는 것을 다운 캐스팅이라고 합니다.

 

instanceof

이렇게 객체 간의 관계가 어지럽게 될 때 이를 확인할 수 있는 타입 확인 연산자인 instanceof로 확인할 수 있습니다.

사용법은 p instanceof Student라고 했을 때 변수가 해당 타입이거나 자식 타입이면 true를 아니면 false를 반환합니다. 

 

매개변수와 다형성

이러한 클래스의 타입은 매개변수로도 사용이 가능합니다. 

⚫자식 객체를 부모 타입 변수에 대입하면 부모 클래스에 선언된 멤버만 볼 수 있음

⚫그런데, 부모 클래스에 선언된 메서드를 자식 클래스가 오버 라이딩했다면, 부모 타 입 변수에 부모 객체의 메서드는 보이지 않고 자식 객체의 메소드가 보임 (동적 바인딩, Dynamic Binding)

⚫그러나 정적 필드와 메소드는 오버 라이딩할 수 없으므로 자식 객체를 부모 타입 변 수에 대입하더라도 부모 타입 변수는 부모 클래스의 정적 필드와 메서드를 보게 됨

 

class Vehicle {
   String name = “탈 것”;
   void whoami() {
      System.out.pringln(“나는 탈 것이다.”)
   }
   static void move() {
      System.out.pringln(“이동하다.”)
   }
}
class Car extends Vehicle {
   String name = “자동차”;
   void whoami() {
      System.out.pringln(“나는 자동차이다.”)
   }
   static void move() {
      System.out.pringln(“달리다.”)
   }
}

// main
Vehicle v = new Car();
System.out.println(v.name);
v.whoami();
v.move();

위 코드 설명

위의 코드 예시는 메서드들을 오버 라이딩해주고 객체를 타입 변환하여 봤을 때 과연 어떤 클래스의 메서드를 따를지를 테스트해보는 코드입니다. 

 

그리고 아까 제가 다형성은 여러 가지를 한꺼번에 처리하기 위함이라고 말씀드렸는데 예시 코드를 하나 보여드리겠습니다.

class Figure {
	void draw() {
		System.out.println("도형을 그린다");
	}
}

class Triangle extends Figure {
	@Override
	void draw() {
		System.out.println("삼각형을 그린다.");
	}
}

class Square extends Figure {
	@Override
	void draw() {
		System.out.println("사각형을 그린다");
	}
}

class Circle extends Figure {
	@Override
	void draw() {
		System.out.println("원을 그린다");
	}
}

public class Ex01 {

	public static void main(String[] args) {
		Figure fig = new Figure();
		Triangle tri = new Triangle();
		Square sq = new Square();
		Circle cir = new Circle();
		
		Figure[] figs = new Figure[] { fig, tri, sq, cir };
		
		for(int i = 0; i <= figs.length;i++) {
			figs[i].draw();
		}

	}

}

이는 가장 상위의 클래스인 Figure로 모든 자식들을 배열로 묶어 출력하는 코드입니다. 

 

이상 상속의 특징인 다형성에 대해 알아봤습니다.

댓글