하위(자식)클래스는 상위 클래스의 메소드를 주어진 그대로 사용할 필요가 없으므로, 하위 클래스는 부모 클래스의 기본적인 동작 방법을 변경할 수 있어야 한다. 여기서 도입된 기능이 바로 메소드 오버라이딩(overriding)이다.
지난 포스트의 Inheritance3
예제를 이용해보자. 클래스 Calculator3의 기본적인 동작 방법을 상속받은 SubstractionableCalculator2에 빼기 기능을 추가했다. 이는 상위 클래스의 기능에 새로운 기능을 추가한 것이다.
만약 상위 클래스에서 물려 받은 메소드 sum을 호출했을 때, 실행 결과는 xx입니다.
하는 결과를 보고싶다면 어떻게 변경해주어야 할까?
class Calculator3 {
int left, right;
// public Calculator3() {}
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
public void avg() {
System.out.println((this.left + this.right) / 2);
}
}
class SubstractionableCalculator2 extends Calculator3 {
public void sum() {
System.out.println("실행 결과는 " + (this.left + this.right) + "입니다.");
}
public void substract() {
System.out.println(this.left - this.right);
}
}
public class Inheritance3 {
public static void main(String[] args) {
SubstractionableCalculator2 c1 = new SubstractionableCalculator2();
c1.setOprands(10,20);
c1.sum();
c1.avg();
c1.substract();
}
}
메소드 sum이 SubstractionableCalculator2에 추가되었다. 실행결과 c1.sum이 상위 클래스(Calculator3)의 메소드가 아닌 하위 클래스(SubstractionableCalculator2)의 메소드 sum을 실행하고 있음을 알수 있다.
하위 클래스에서 상위 클래스와 동일한 메소드를 정의하면, 부모 클래스로부터 물려 받은 기본 동작 방법을 변경하는 효과를 갖게 된다. 이것을 메소드 오버라이딩(overriding)이라고 한다.
상위 클래스의 메소드 avg가 계산 결과를 리턴해주는 방법이 되게끔 SubstractionableCalculator2
클래스에 코드를 추가해보자.
public int avg() {
return (this.left + this.right)/2;
}
위의 코드를 추가하고나면 int에서 오류가 발생한다.
overriding을 하기 위해서는 메소드의 반환 형식이 같아야한다. 다음의 조건을 충족시켜야만 오버라이딩이 가능한 것이다.
- 메소드의 이름
- 메소드 매개변수의 숫자, 데이터 타입, 순서
- 메소드의 리턴 타입
이렇게 메소드의 형태를 정의하는 것을 메소드의 서명(signature)라고 한다. 위의 에러는 메소드들 간의 서명이 달라서 발생한 문제이다. 조건에 맞게 다시 코드를 작성해보자.
// Calculator의 메소드 avg
public int avg() {
return ((this.left + this.right)/2);
}
// SubstractionableCalculator2의 메소드 avg
public int avg() {
return ((this.left + this.right)/2);
}
코드를 이렇게 수정해주면 메소드 오버라이딩이 가능하다. 그러나 각 클래스 속 메소드avg가 중복임을 알 수 있다. 부모와 자식 클래스가 같은 로직을 가지고 있으니, 이러한 중복을 제거할 필요가 있다.
SubstractionableCalculator2의 메소드 부분을 수정해보자.
public int avg() {
return super.avg();
}
이렇게 super
를 이용하면 중복되는 코드를 제거할 수 있다.
우리는 계산기 예제에서 2개의 값을 입력받도록 코드를 작성했다. 3개의 입력값도 받으려면 어떻게 해야할까? 메소드 setOprands3를 새롭게 정의하고 매개변수를 3개로 해주는 방법이 있을 수 있겠지만 다른 방법을 보자.
class Calculator_o{
int left, right;
int third = 0;
public void setOprands(int left, int right){
System.out.println("setOprands(int left, int right)");
this.left = left;
this.right = right;
}
public void setOprands(int left, int right, int third){
System.out.println("setOprands(int left, int right, int third)");
this.left = left;
this.right = right;
this.third = third;
}
public void sum(){
System.out.println(this.left+this.right+this.third);
}
public void avg(){
System.out.println((this.left+this.right+this.third)/3);
}
}
public class overloading {
public static void main(String[] args) {
Calculator_o c1 = new Calculator_o();
c1.setOprands(10, 20);
c1.sum();
c1.avg();
c1.setOprands(10, 20, 30);
c1.sum();
c1.avg();
}
}
위의 코드를 실행해보면 매개변수의 개수에 따라서 같은 이름의, 서로 다른 메소드를 호출하고 있음을 알 수 있다.
메소드 오버로딩(overloading) : 이름은 같지만 시그니처는 다른 메소드를 중복으로 선언할 수 있는 방법
메소드 오버로딩은 매개변수를 이용한다. 매개변수가 다르면 이름이 같아도 서로 다른 메소드가 되는 것이다.
그러나 매개변수는 같지만 리턴 타입이 다르면 오류가 발생한다.
public class overloading {
void A (){
System.out.println("void A()");
}
void A (int arg1){
System.out.println("void A (int arg1)");
}
void A (String arg1){
System.out.println("void A (String arg1)");
}
//int A (){System.out.println("void A()");}
public static void main(String[] args) {
overloading od = new overloading();
od.A();
od.A(1);
od.A("coding everybody");
}
}
void A()
와 void A (int arg1)
: 매개변수의 숫자가 다름void A(int arg1)
과 void A(String arg1)
: 매개변수의 데이터 타입이 다름상속의 관계에서도 오버로딩은 쓸 수 있다.
public class overloading2 extends overloading{
void A (String arg1, String arg2){
System.out.println("sub class : void A (String arg1, String arg2)");
}
void A (){
System.out.println("sub class : void A ()");
}
public static void main(String[] args) {
overloading2 od = new overloading2();
od.A();
od.A(1);
od.A("coding everybody");
od.A("coding everybody", "coding everybody");
}
}
overloading2는 overloading을 상속받고 있다. void A (String arg1, String arg2)
는 overloading에 정의되어 있지 않으므로 메소드 오버로딩이 된다.
반면 void A()
는 매개변수가 없으며, overloading에 이미 매개변수가 없는 A가 존재한다. 이 둘은 매개변수의 형태가 같으므로 overriding에 해당한다.
Reference
1. 생활코딩_overriding
2. 생활코딩_overloading