Java

overriding

창의적인 상속

상속은 상위 클래스의 기능을 하위 클래스에게 물려주는 기능이다. 그렇다면 하위 클래스는 상위 클래스의 메소드를 주어진 그대로 사용해야 할까? 만약 그래야 한다면 제약이 상당할 것이다. 이런 제약을 벗어나려면 하위 클래스가 부모 클래스의 기본적인 동작방법을 변경할 수 있어야 한다. 이런 맥락에서 도입된 기능이 메소드 오버라이딩(overriding)이다.

상속 시간의 예제를 살펴보자. 이 예제는 클래스 Calculator의 기본적인 동작 방법을 상속 받은 SubstractionableCalculator에 빼기 기능을 추가하고 있다. 이것은 상위 클래스의 기능에 새로운 기능을 추가한 것이다. 만약 상위 클래스에서 물려 받은 메소드 sum을 호출했을 때 아래와 같이 그 결과를 좀 더 친절하게 알려줘야 한다면 어떻게 해야할까?

실행 결과는 30입니다.

상속 토픽의 예제를 조금 변경해보자.

package org.opentutorials.javatutorials.overriding.example1;

class Calculator {
    int left, right;

	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 SubstractionableCalculator extends Calculator {
	
	public void sum() {
		System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
	}
	
	public void substract() {
		System.out.println(this.left - this.right);
	}
}

public class CalculatorDemo {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

아래는 차이점이다.

실행결과는 아래와 같다.

실행 결과는 30입니다.
15
-10

메소드 sum이  SubstractionableCalculator에 추가 되었다. 실행결과는 c1.sum이 상위 클래스의 메소드가 아니라 하위 클래스의 메소드 sum을 실행하고 있다는 것을 보여준다. 하위 클래스 입장에서 부모 클래스란 말하자면 기본적인 동작 방법을 정의한 것이라고 생각할 수 있다. 하위 클래스에서 상의 클래스와 동일한 메소드를 정의하면 부모 클래스로부터 물려 받은 기본 동작 방법을 변경하는 효과를 갖게 된다. 기본동작은 폭넓게 적용되고, 예외적인 동작은 더 높은 우선순위를 갖게하고 있다. 이것은 공학에서 일반적으로 발견되는 규칙이다. 이것을 메소드 오버라이딩(overriding)이라고 한다.

오버라이딩의 조건

상위 클래스에서 정의하고 있는 메소드 avg는 계산 결과를 화면에 출력하고 있다. 그런데 계산 결과를 좀 더 다양하게 사용하기 위해서 메소드 avg가 화면에 결과를 출력하는 대신 계산 결과를 리턴해주면 좋겠다. 그래서 아래와 같이 코드를 고쳐봤다.

package org.opentutorials.javatutorials.overriding.example1;

class Calculator {
    int left, right;

	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 SubstractionableCalculator extends Calculator {
	
	public void sum() {
		System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
	}
	
	public int avg() {
		return (this.left + this.right)/2;
	}
	
	public void substract() {
		System.out.println(this.left - this.right);
	}
}

public class CalculatorDemo {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

이것은 아래와 같은 에러를 발생한다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The return type is incompatible with Calculator.avg()

	at org.opentutorials.javatutorials.overriding.example1.SubstractionableCalculator.avg(CalculatorDemo.java:26)
	at org.opentutorials.javatutorials.overriding.example1.CalculatorDemo.main(CalculatorDemo.java:40)

overriding을 하기 위해서는 메소드의 리턴 형식이 같아야 한다. 즉 클래스 Calculator의 메소드 avg는 리턴 타입이 void이다. 그런데 이것을 상속한 클래스 SubstractionableCalculator의 리턴 타입은 int이다. 오버라이딩을 하기 위해서는 아래의 조건을 충족시켜야 한다.

  • 메소드의 이름
  • 메소드 매개변수의 숫자와 데이터 타입 그리고 순서
  • 메소드의 리턴 타입

위와 같이 메소드의 형태를 정의하는 사항들을 통털어서 메소드의 서명(signature)라고 한다. 즉 위의 에러는 메소드들 간의 서명이 달라서 발생한 문제다. 아래와 같이 상위 클래스의 코드를 변경해서 이 문제를 우회하자.

package org.opentutorials.javatutorials.overriding.example1;

class Calculator {
    int left, right;

	public void setOprands(int left, int right) {
		this.left = left;
		this.right = right;
	}

	public void sum() {
		System.out.println(this.left + this.right);
	}

	public int avg() {
		return ((this.left + this.right) / 2);
	}
}

class SubstractionableCalculator extends Calculator {
	
	public void sum() {
		System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
	}
	
	public int avg() {
		return ((this.left + this.right) / 2);
	}
	
	public void substract() {
		System.out.println(this.left - this.right);
	}
}

public class CalculatorDemo {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

차이점은 아래와 같다.

상위 클래스와 하위 클래스의 서명이 같기 때문에 메소드 오버라이딩을 할 수 있었다. 그런데 위의 코드를 보면 중복이 발생했다. 메소드 avg의 부모와 자식 클래스가 같은 로직을 가지고 있다. 중복은 제거 되어야 한다. 생성자와 마찬가지로 super를 사용하면 이 문제를 해결 할 수 있다.

package org.opentutorials.javatutorials.overriding.example1;

class Calculator {
    int left, right;

	public void setOprands(int left, int right) {
		this.left = left;
		this.right = right;
	}

	public void sum() {
		System.out.println(this.left + this.right);
	}

	public int avg() {
		return ((this.left + this.right) / 2);
	}
}

class SubstractionableCalculator extends Calculator {
	
	public void sum() {
		System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
	}
	
	public int avg() {
		return super.avg();
	}
	
	public void substract() {
		System.out.println(this.left - this.right);
	}
}

public class CalculatorDemo {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		System.out.println("실행 결과는" + c1.avg());
		c1.substract();
	}
}

차이점은 아래와 같다.

하위 클래스의 메소드 avg에서 상위 클래스의 메소드를 호출하기 위해서 super를 사용하고 있다. 덕분에 코드의 중복을 제거 할 수 있었다.

이렇게해서 부모 클래스의 기능을 변경 할 수 있는 방법인 메소드 오버라이딩에 대해서 알아봤다.

댓글

댓글 본문
작성자
비밀번호
  1. 마린조아
    실제 업무에서 어떻게 쓰일지 궁금해 지는 강의이네요.
    이 강의 자체만으로는 오버라이딩을 하기 위해서 부모클래스의 메소드를 수정해야 하는 문제가 있는데 만약에 부모클래스를 직업적으로 이용하던 클래스들은 그로 인해 생기는 연관된 문제에 대해서는 언급이 없어서요.
    오히려 그게 더 문제가 될거 같습니다. 상당히 이용에 제약이 생길 수 밖에 없는 개념이네요.
    물론 부모클래스의 수정 후 에도 별다른 문제가 없고 자식클래스에서 오버라이딩해서 super 하단 소스에 추가적인 사항을 적용해서 이용할 수 있는 상황이라면 유용해 보입니다.
  2. 부모와 자녀 사이
    오버라이딩 시 부모 메소드와 자식 메소드의 시그니처가 동일해야하고, 그렇기 때문에 위 영상에서는 부모쪽 시그니처를 변경해서 문제를 해결하셨는데...

    다른 사람이 만들어놓은 클래스를 건들지 않아야 한다고 알고있습니다.
    부모 클래스 자체를 다른 사람이 만들어 놓고 내가 상속받아 오버라이딩 해야하는 상황에 위와 같은 문제발생 시
    어떻게 해결해야 할까요?
  3. Weaver
    강의 감사합니다~
  4. DoitDoit
    많은 도움이 되었습니다!^^
  5. Jay Jaewoong Kang
    고맙습니다.
  6. JustStudy
    고맙습니다
  7. 김트라슈
    감사합니다
  8. 이마본
    자식 클라스에서 오버라이딩을 할때 추가되는 부분이 분명 존재 할 것이고 추가되는 부분이 있다는 말은 기존의 부모 메소드의 소스내용을 그대로 적어야되는 부분이 있을텐데 만약 그부분이 1000줄에 해당하는 양이라면 그때 super를 쓰는 것이 제일 효율적인 방법이겟죠? 1000줄의 해당하는 부분을 그대로 적는다면 유지,보수,가독성에서 매우 안좋은 코딩이 될테니까요
    대화보기
    • 감사합니당
    • 찐똥구리구리
      class SubstractionableCalculator extends Calculator {

      public void sum() {
      System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
      }

      public void avg2() {
      int a = super.avg();
      System.out.println("평균값은 "+ a + "입니다.");
      }

      public void substract() {
      System.out.println(this.left - this.right);
      }
      }

      public class OverridingDemo2 {
      public static void main(String[] args) {
      SubstractionableCalculator c1 = new SubstractionableCalculator();
      c1.setOprands(10, 20);
      c1.sum();
      c1.avg2();
      c1.substract();
      }
      }

      super.avg()를 이런 식으로 사용하는 걸까요?? 상상해봤는데, 우선 작동은 하네요..ㅎㅎㅎ
      재미집니다...
    • 레니타키
      강의 내용에도 있듯이 기존함수기능과 더불어 추가기능을 코딩할 수 있어서인거 같네요.
      대화보기
      • 오빠는다르다
        감사합니다!!!!
      • 나도 궁금
        저도 루니스트 님과 같은 궁금증이 드네요.

        굳이 super를 써서 상위 클래스의 메소드를 호출하는 이유는 무엇인가요? 오버라이딩을 안해도 될텐데 결론은 섹스
      • 루니스트
        super을 쓸거면 아예 오버라이딩을 안하는 것이 효율적인 것 아닌가요??
        어차피 부모 클래스의 함수를 쓰면 되는 경우 인 것 같은데.
      • 지나가던
        Byunghawk Lee // 실제 프로그램을 제작하는 과정을 보면
        '많은' 사람들이
        '엄청나게 많은 코드'를
        '이곳 저곳의 여러 장소, 여러 파일'에서
        '여러 다른 사람들의 도움을 받아가며' 작업을 하기 때문이죠.
        그래서 이런 기법들이 필요한거랍니다. ㅎ
        대화보기
        • 박첩구드
          결국 오버라이딩은 하위클래스에서 상위클래스에서 상속받은 메소드의 기능을 바꾸고 싶을 때 쓰는 것이군요ㅎㅎ 정말 명강의입니당!
        • Byunghawk Lee
          흠~~
          나중에 실제 코딩을 할 때 왜 이러한 것이 필요한 지 깨닳고 다시 봐야할 듯 싶습니다.
          지금 저는 C 나 Visul Basic 같은 언어를 기본적인 것만 배워서 깊이는 모르는데 뭔가 복잡하다 생각이 드네요. 전문 프로그래머에게 필요한가?하는 생각이 들어서 ....
          아무튼 이 부분은 그냥 대강 이해하고 넘어갑니다.
        • NewRun
          네 맞습니다.~

          부모클래스, 자식클래스 둘다 그 어디에도 System.out,println(); 이라는 출력문이 없기 때문에

          main 메소드에서 c1.avg()를 출력하지 못하는 것 입니다.
          대화보기
          • NewRun
            안녕하세요. 이고잉님 ~

            강의를 듣다가

            질문) overring 조건 중에 부모, 자식 로직이 동일하면 안된다고 해서 avg()에서는 super를 사용하였습니다.

            그런데 sum()은 부모, 자식 클래스에서 메소드 로직이 동일한데 출력이 되는 이유는
            SYSO 라서 관계없는건가요???
          • cocohodu
            좋은강의 감사합니다
          • 지나가던 초보
            system.out.println(); 이 명령어가 화면에 변수 or 문자를 출력하는 명령어 입니다.
            그렇기 때문에 system.out.println( c1.avg() );를 하면
            c1.avg(); 를 통해서 받아온 변수를 화면에 출력하라는 명령이 되는것입니다.
            c1.avg(); 이 명령만 하면 변수는 받아왔지만 출력하라는 명령이 없기 때문에 화면에 표시하지 않는 것이지요.

            참고로

            int a = c1.avg();
            system.out.println(a); 를 하면 값이 출력됩니다.
            대화보기
            • nedaum
              저도 초보지만 제생각은System.out.println("실행 결과는 " + c1.avg());
              은 출력문이니까 당연히 c1.avg()의값이 출력되죠
              근데 왜 c1.avg();만하면 값이 출력되지않냐면
              avg()메소드안에 출력문이 없고 값만 리턴해서 출력이안되는것같습니다
              만약 avg()메소드에 출력문이있다면 값이 출력될겁니다
              대화보기
              • 강시성
                main에서
                System.out.println("실행 결과는 " + c1.avg()); 출력됨
                그런대 c1.avg(); 만 실행 하면
                값이 출력되지 않는 이유가 무엇인지요?.
              • thexl
                strictly하게 라고 해도 상관없을 거 같은데ㅎㅎ;

                그렇게 치면 "sexy한 여자"는 해석하면 "섹시한한 여자"라고 되버리는데

                그렇다고 "sexy의 명사형+한 여자" 를 쓰면 이상하자나영ㄷㄷ
                대화보기
                • 개발괴발
                  감사합니다 잘 보았어요
                • Seungmo Song
                  ljh// 네 평균값만 구하는 이 경우엔 그렇지만 자식클래스 오버라이딩 시 기존 것을 이용하면서(super.avg()식으로) 뒤에 어떠한 코드를 추가할 때를 대비해 가르쳐주신 것 같습니다.
                  대화보기
                  • 위 마지막 예제 같은 경우는 그냥 SubstractionableCalculator 클래스 경우 avg 메소드를 빼버려도 상관 없는거죠???
                  • 최재필
                    이번 강의의 주제인 오버라이딩을 설명하시려고 그런 것 아닐까요.
                    오버라이딩이란 부모클래스의 메소드를 수정하여 사용하는 것이니까요 ^^
                    대화보기
                    • 환글
                      좋은 강의 고맙습니다.
                    • jeyul
                      wonbae Kwon님 말씀처럼, SubtractionableCalculator 클래스는 상위 클래스의 변수와 메소드를 그대로 상속 받았기 때문에 굳이 avg 메소드를 재정의할 필요는 없네요.
                      오버라이딩을 위한 조건과 super의 설명으로 받아 들이겠습니다.
                    • Haewon Lee
                      @Override를 붙여도 되고 안 붙여도 되는데
                      @Override를 붙이면 가독성이 더 좋아지기 때문에 붙이는거죠??
                      또 다른 이유가 있나요??
                      대화보기
                      • YOUNG
                        좋은 조언 감사합니다.

                        Annotation 사용법을 모르시는 분들을 위해서:

                        class SubstractionableCalculator extends Calculator {
                        @Override public void sum() {
                        System.out.println("실행 결과는 " +(this.left + this.right)+"입니다.");
                        }
                        @Override public int avg() {
                        return super.avg();
                        }
                        public void substract() {
                        System.out.println(this.left - this.right);
                        }
                        }

                        여기에 좋은 설명이 나와있네요: http://www.javapractices.com......223
                        대화보기
                        • wonbae Kwon
                          위에서 보면 setOprands 맷소드는 상속받은 클래스에서 재선언 하지 않고 Demo클래스에서 쓸수 있는데
                          왜 굳이 avg는 super을 써서 선언을 다시 해준건가요? Demo에서 그냥 써도 되지않나요?
                        • seokhyung Jang
                          return super 의 기능은 부모클래스의 기능에 자식클래스의 기능을 "추가" 만 가능한것이고
                          "변경"은 불가능 한거에요???ㅎㅎ
                          이고잉님의 강의를 듣다가 부모클래스를 오버라이딩 하기위해선 자식 클래스에eturn super.avg(); 를써주어야
                          한다고 하여서.. 제 혼자 생각에 "어 그럼 저렇게 같을거면 궅이 자식클래스에 호줄해서 다시 부모 클래스에다가
                          리턴할필요는 없지안나 " 라고 생각하는 중에 이고잉님이 "뒤에다가 추가하면 됩니다" 이렇게 말씀해주셔서
                          해결이 되었지만.. 다른 한편으론 추가말고 변경도 가능한지 궁금해서요..ㅎㅎ 왠지 안될것같지만..그래도
                          궁금해서요 ^^
                        • egoing
                          피드백 고맙습니다. 어노테이션에서 다뤄보겠습니다. ^^
                          대화보기
                          • 미친고양이
                            정말 잘 보고있습니다 ^^;
                            @Override 라는 어노테이션을 사용하여 좀더 strictly 하게 코드 작성하는것도
                            좋은 습관이 될것같습니다...
                          버전 관리
                          egoing
                          현재 버전
                          선택 버전
                          graphittie 자세히 보기