❗ 개인적으로 공부했던 내용을 복습하고 정리하기 위한 글입니다! 따라서 내용이 정확하지 않을 수 있습니다!
자바의 정석 제 3판을 보고 공부한 것을 정리한 글입니다!
다형성(多形性, polymorphism; 폴리모피즘)은 그 프로그래밍 언어의 자료형 체계의 성질을 나타내는 것
다형성을 위키백과에 검색했을 때, 나온 다형성의 뜻이다. 사실 이렇게만 보면 이게 무슨 소리지, 하게 된다. 한국인 답게 한자를 하나하나 까보면, 많을 다(多), 모양 형(形), 성품 성(性)으로 쓴다. 쉽게 말해서 다양한 형상을 가진 성질이라고 이해하면 될 것 같다.
그렇다면 자바에서의 다형성은 무엇일까? 자바에서 다형성은 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 구현하였다. 즉, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다는 것이다. 즉 자바에서는 상속을 통해 주로 다형성을 구현하곤 한다. 상속에 대한 내용은 여기에 정리해 두었었다.
그런데 나는 꼭 상속만이 다형성이라고 생각하지는 않는다. 위에 한자를 해석했듯, 다양한 형상을 가진 성질인데, 우리가 주로 사용하는 오버로딩, 오버라이딩 또한 다형성의 일부라고 생각한다. 오버로딩과 오버라이딩 둘 다 같은 이름의 메소드를 쓰지만, 수행되는 기능이 다르기 때문이다.
오버로딩(overloading)은 한 클래스 내에 같은 이름의 메소드를 여러개 정의하는 것
또한 오버로딩을 성립시키기 위해서는 메소드 이름이 같아야 하고, 매개변수의 개수 또는 타입이 달라야 한다.
우리가 가장 흔히 볼 수 있는 오버로딩의 예시로는 바로 println()
메소드가 있다. println()
메소드에 내가 출력을 원하는 변수를 매개변수로 아무거나 넣어도 다 출력이 된다. 이런 경우, 매개변수의 타입이 달라져 오버로딩이 성립되었다. 실제로 PrintStream.class
에 println()
이 매개변수만 다르고, 여러개 정의되어 있는 것을 볼 수 있다.
또 우리가 흔히 볼 수 있는 오버로딩은, 바로 클래스에 선언한 생성자이다. 생성자도 일종의 메소드로, 우리가 매개변수의 개수만 다르게 두어, 오버로딩을 통해 필요한 생성자를 적재적소에 맞게 사용한다.
public class Member {
private String name;
private int age;
public Member() { // 매개변수의 개수가 달라 오버로딩이 일어났다.
super();
}
public Member(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Member [name=" + name + ", age=" + age + "]";
}
}
궁극적으로 오버로딩을 쓰는 이유는 하나의 메소드 이름만 기억하면 되기 때문에 오류의 가능성을 많이 줄이고, 메소드 이름이 같다면 같은 기능을 하는 메소드라고 추론이 가능하다. 또한 메소드 이름짓기에 걸리는 시간을 줄일 수 있다. 아래와 같은 사진처럼, 생각 의외로 변수나 메소드 이름을 짓는 데에 많은 시간을 소비하곤 한다. 이 시간을 줄일 수 있다는 것은 커다란 장점이라고 생각한다.
부모 클래스로부터 상속받은 메소드의 내용을 변경하는 것
보통 상속받은 메소드를 그대로 쓰는 경우도 있지만, 자식 클래스가 자신에게 맞게 변경하는 경우, 부모 클래스의 메소드를 오버라이딩 하여 사용한다. 메소드 오버라이딩의 조건은, 이름이 같아야 하고 매개변수가 같아야하고, 반환타입이 같아야 한다.
아래 코드는 Parent.java
를 상속받는 Child.java
의 코드이다.
// Parent.java
public class Parent {
private int x;
private int y;
public Parent() {}
public Parent(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String display() {
return "x : " + x + ", y : " + y;
}
}
// Child.java
public class Child extends Parent {
private int z;
public Child() {}
public Child(int x, int y, int z) {
super(x, y);
this.z = z;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public String display() { // 부모의 display()를 오버라이딩
return super.display() + ", z : " + z;
}
}
// Run.java
public class Run {
public static void main(String[] args) {
Child c = new Child();
c.setX(10);
c.setY(20);
c.setZ(30);
System.out.println(c.display());
}
}
먼저 Parent.java
안에 display()
라는 메소드를 정의했고, Child.java
를 만들 때 dispaly()
라는 메소드를 오버라이딩하여 다시 정의했다. 이 때, display()
메소드를 구현할 때에는, 부모의 display()
메소드를 호출하고, 그 뒤에 온전히 자신의 것인 z를 출력하기 위한 출력문을 덫붙였다. 그 후 메인 메소드에서 자식 클래스인 객체 c를 생성하고, setter
를 통하여 값을 저장한 뒤, c의 dispaly()
메소드를 호출하면, x, y, z의 값이 모두 다 출력되는 것을 확인할 수 있다.
이 코드에서는 display()
로 메소드를 만들었지만, 우리가 흔히 사용하는 toString()
메소드 또한 최상의 클래스인 Object.class
에 정의되어 있는 메소드를 오버라이딩 되어 사용하는 것이다.
두번째 스터디 주제인 다형성에 대해 정리했다! 다형성은 말 그대로 이름은 같고, 다른 기능을 하는 것으로 알고 있었는데, 책에는 상속에 이어서 upcasting, downcasting만 범위에 있었고, 내가 다형성이 가장 잘 보이는 예시라 생각하는 오버로딩과 오버라이딩에 대한 내용은 없어서 따로 추가했다. 말이 한자여서 어렵지, 사실 우리는 자바를 배우면서 꽤나 많은 다형성을 적용해 코딩해왔다는 걸 알 수 있었다! 아무튼 간략하게 내용을 정리해봤는데, 주로 헷갈리는 오버로딩과 오버라이딩에 대하여 다시 정리할 수 있었다.
끝!😖