다형성이란
다형성이란 객체 지향 프로그래밍의 특징 중 하나로, 하나의 메소드나 클래스로 다양한 결과를 만드는 것을 말한다.
다형성의 예로는 Overloading(오버로딩), Inheritance(상속), Interface(인터페이스) 등이 있다.
Overloading
오버로딩은 지난번 글 오버라이딩과 오버로딩에서 설명하기도 했다.
오버로딩이 다형성의 예는 아니라는 말도 존재하긴 한다.
System.out.println
은 본질적으로 출력한다는 결과는 같을 수 있지만, 매개변수의 타입에 따라 코드의 동작은 다르다.
같은 이름이지만 다른 동작을 갖는 이러한 성질이 다형성의 한 예라고 볼 수 있다.
Inheritance
오버라이딩도 메소드 하나로 상속한 클래스마다 다른 결과가 나타나기 때문에 다형성의 예로 볼 수 있다.
이 외에 상속과 관련해 객체를 생성할 때도 다형성을 확인할 수 있는데, 상속과 오버라이딩으로 인해 가능한 특징 중 하나이다.
아래 코드를 한번 살펴보자.
Object Type Conversion
class A{
public String className(){
return "A";
}
}
class B extends A{
@Override
public String className(){
return "B";
}
public void printclassName(){
System.out.println("B");
}
}
public class Example{
public static void main(String[] args){
A object = new B();
System.out.println(object.className());//결과: B
object.printclassName();//결과: 컴파일 에러
}
}
보통 우리는 객체를 생성할 때 A object = new A();
와 같이 생성한다.
그런데 위의 코드는 조금 다르다. A 타입 변수에 B 객체를 생성해주었다.
이렇게 객체를 생성하게 되면 object
는 A 클래스에 존재하는 메소드와 필드만 사용할 수 있다.
그러나 오버라이딩한 메소드는 B 클래스에 존재하는 메소드 형태로 사용이 가능하다.
예를 들어 위 코드에서 object.className
를 실행했을 때 B가 반환되고, B 클래스에 존재하는 printclassName() 컴파일 에러가 발생해 사용할 수 없다.
이런 방식의 객체 생성이 과연 어디에 쓰일까 하는 생각이 들 수 있다.
아래 코드를 보면 어느정도 이해가 갈 것이라고 생각한다.
class BaseClass{
public String className(){
return "BaseClass";
}
}
class B extends BaseClass{
@Override
public String className(){
return "B";
}
...
}
class C extends BaseClass{
@Override
public String className(){
return "C";
}
}
public class Example{
public static void printClassName(BaseClass A){
System.out.println(A.className());
}
public static void main(String[] args){
BaseClass b = new B();
BaseClass c = new C();
printClassName(b);//출력결과 B
printClassName(c);//출력결과 C
}
}
만약 printClassName의 매개변수 타입이 BaseClass가 아니라면, 위와 같은 처리는 불가능할 것이다.
만약 BaseClass를 배열로 생성한다면 printClassName과 같은 함수도 한꺼번에 처리할 수 있다.
이렇게 하나의 클래스(BaseClass)가 B, C라는 다양한 결과를 만들어 내므로 이도 다형성의 예로 볼 수 있다.
Interface
오버라이딩과 비슷하게, 하나의 인터페이스 클래스로 다양한 클래스라는 결과를 만들어낼 수 있으므로 이 또한 다형성의 예로 들 수 있다.
이 외에 인터페이스도 위의 예인 클래스 타입 변환처럼 사용할 수 있다.
아래의 코드를 한번 살펴보자.
interface ABC{
public String ABC();
}
interface DEF{
public String DEF();
}
class Test implements ABC, DEF{
public String ABC(){
return "ABC";
}
public String DEF(){
return "DEF";
}
}
public class Example{
public static void main(String[] args){
Test testing = new Test();
ABC testing2 = new Test();
DEF testing3 = new Test();
testing.ABC();
testing.DEF();
testing2.ABC();
testing2.DEF();//컴파일 에러
testing3.ABC();//컴파일 에러
testing3.DEF();
}
}
앞선 예제에서 부모 클래스 타입으로 자식 클래스를 생성했듯이, 인터페이스 타입으로 해당 인터페이스를 참조한 클래스를 생성할 수 있다.
이를 통해 해당 인터페이스에 존재하는 메소드만 사용할 수 있도록 제한이 가능해진다.
이렇게 하나의 클래스가 인터페이스를 활용해 제한되는 성질도 다형성의 예로 볼 수 있다.
다형성을 한 문장으로 설명하라면 항상 어렵다. 이와 관련된 개념들이 머릿 속에 둥둥 떠다니는 것 같다.
오늘 정리를 통해서 좀 더 기억에 오래 남았으면 좋겠다.
다형성이란 하나로 여러 가지 결과를 만들 수 있는 것을 뜻한다는 것을 꼭 기억하자!