[자바(JAVA)] 다형성 Polymorphism 깨부수기

오늘 자바 공부 최대의 난적을 만났다.
자바를 공부하는 사람은 두 부류로 나뉜다고 한다.
다형성을 이해한 사람과 다형성을 이해하지 못한사람….

다행히 C++ 수업을 들으면서 다형성에 대해서 공부를 했던 기억이 조금은 남아있어서,
자바에서의 다형성에 대해서 이해하는데 도움이 되었던 것 같다. 정리해보자

다형성이란
하나의 이름으로 여러개의 형태를 구성할 수 있는 특징을 말한다.
다형성에는 Method Polymorphism 과 Type Polymorphism이 있다.

우선 Method Polymorphism에는 OverridingOverloading이 있다.

오버라이딩(overidding) 은 상속 관계에서 부모 클래스의 메소드를 자식 클래스에서 재정의 하는것을 말한다.
오버로딩은 (overloading)은 하나의 클래스나 상속받은 클래스 내에 같거나 비슷한 기능의 메소드의 이름을 같게 정의하여 사용하는 것을 말한다.
메소드 이름은 같게 파라미터는 다르게 하여 사용한다.

이게 뭔 소리냐면

package com.polymorphism.overriding;

class Parent{

	String firstname = "Lyoo";

	void hi(){
		System.out.println("Parent: Hi");
	}
}

class Child extends Parent{

	void hi(){ //오버라이딩
		System.out.println("Child: Hi");
	}
}

class Child2 extends Parent{
}

public class OverridingTest{
	public static void main(String[] args) {
		Child c = new Child();//Child클래스는 hi 메소드를 오버라이딩 함
		c.hi();
		Child2 c2 = new Child2(); //오버라이딩 하지 않
		c2.hi();
	}
}

먼저 Parent 클래스는 hi 라는 메소드를 가지고 있다.
Parent 클래스를 상속받는 Child 클래스는 원래 아무것도 하지 않아도 Parent클래스 firstname 이라는 필드와 hi 라는 메소드를 물려 받는다.
그런데 Child 클래스에서 hi 메소드를 재정의 하고 있다. 메소드 이름과 리턴타입, 파라미터가 동일한 함수를 {} 블럭 안의 동작만 수정해서 작성하였다. 이게 오버라이딩 이다. 여기에 추가적으로 접근제어자는 부모 메서드와 같거나 더 넓어야한다.

이렇게 재정의를 하게되면 무엇이 달라지나? Parent를 상속받고 아무것도 하지 않은 Child2를 만들어서 hi 메소드를 실행 시켜보자.

Child 객체에서 hi 메소드를 실행 시키면 Child: Hi 라는 결과가 나오지만 Child2 객체에서 hi 메소드를 실행시키면 부모 메서드의 hi가 실행되어 Parent: Hi 라는 결과가 나온다.

오버라이딩은 생각보다 간단하다. 부모 클래스의 메소드를 자식에서 재정의 하는것! 끝~


이제 오버로딩이다.

package com.polymorphism.overloading;

class Parent{

	String firstname = "Lyoo";

	void hi(){
		System.out.println("Parent: Hi");
	}
}

class Child extends Parent{

	void hi(){ //오버라이딩
		System.out.println("Child: Hi");
	}

	void hi(String who){ //오버로딩
		System.out.println("Child: Hi "+who);
	}

	void amChild(){
		System.out.println("I am child");
	}
}

class GrandChild extends Child{

	void hi(){
		System.out.println("GrandChild: Hi");
	}
}

public class OverloadingTest{
	public static void main(String[] args) {
		Child c = new Child();
		c.hi(); // 파라미터가 없는 hi 메소드(오버라이딩 메소드)
		c.hi("clazitive"); //String을 파라미터로 받는 hi 메소드(오버로딩 메소드)        
	}
}

오버로딩은 오버라이딩과 다른점이 몇가지 있다. 오버로딩은 메소드 이름은 같게, 파라미터는 다르게 정의한다.
위의 예시에서 Child 클래스를 살펴보면 입력이 없는 void hi() 와 문자열을 입력받는 void hi(String) 이 있다. 이게 오버로딩의 한 예시이다. 두 메서드는 공통적으로 Hi라는 의미를 가진 문자열을 출력한다. 비슷한 기능의 메서드를 이름을 같게 정의함으로써 메서드 이름을 짓느라 고민할 필요도 없고, 좀더 직관적으로 코딩을 할 수 있다.


Type Polymorphism은 부모 Type으로 자식 Type의 객체를 참조할 수 있는 것을 말한다.

package com.polymorphism;

class TypePolymorphism{
	public static void main(String[] args) {
		Parent p1 = new Parent();
		Parent p2 = new Child();
		Parent p3 = new GrandChild();
		Child c1 = new Child();

		p1.hi();
		p2.hi();
		p3.hi();
		c1.amChild();
		//p2.amChild();//	The method amChild() is undefined for the type Parent
	}
}

부모Type으로 자식 Type의 객체를 참조할 수 있다는 말은 위처럼 Parent타입으로 Parent객체 뿐만 아니라, Child, GrandChild 객체까지 참조할 수 있다는 말이다.

package com.polymorphism;

class Parent{

	String firstname = "Lyoo";

	void hi(){
		System.out.println("Parent: Hi");
	}
}

class Child extends Parent{

	void hi(){ //오버라이딩
		System.out.println("Child: Hi");
	}

	void hi(String who){ //오버로딩
		System.out.println("Child: Hi "+who);
	}

	void amChild(){
		System.out.println("I am child");
	}
}

class GrandChild extends Child{

	void hi(){
		System.out.println("GrandChild: Hi");
	}
}

그렇다면 메소드 실행은 어떻게 될까?
좌변의 참조하는 객체를 리모컨이라고 생각하면 되는데, 여기서 오버라이딩 된 메서드에 한해서 객체의 오버라이딩 된 메서드가 호출된다.

특별히 p2 의 경우 Parent 타입으로 Child를 참조하고 있다.
이때 Parent 의 hi 를 Child가 오버라이딩 하고 있는데, 이경우 p2에서 hi 메소드를 실행하면, Parent의 hi 가 아니라 Child의 오버라이딩 됀 hi가 호출된다.
p3에서 hi를 호출하면 어떤 결과가 나올까?

p3도 마찬가지로 GrandChild에서 hi를 오버라이딩 하고 있다. 그렇기 때문에 오버라이딩 됀 hi가 호출된다.

한가지 주의 해야할점은 p2.amChild()의 경우 Parent 타입으로 Child 객체를 참조하고 있는데, Parent타입에는 amChild() 라는 메서드가 없기 때문에 오류가 발생한다.
amChild라는 메서드를 사용하려면 Child타입으로 객체를 참조해야한다.
Parent 리모컨 에는 hi 라는 메서드 밖에 없는 것이다.

여기까지 간단하게(?) 다형성에 대해서 정리해봤는데, 앞으로 나오는 추상클래스나 인터페이스 개념을 공부하기에 앞서 잘 정리할 필요가 있다. 코드 부분은 추후에 정리해서 깃허브에 올리고 gsnippet으로 보기좋게 정리할 예정이다. gsnippet 정리 완료~