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. 이게
    오버라이딩의 조건을 설명하기 위한건 좋은데, 그 설명을 위해 실제로 사용하지 않는 예제를 활용하는건 적절하지 않으며 오해를 불러일으킬수 있다고 생각합니다. 실제로 이런 경우 어떤식으로 우회하는지를 보여주셨다면 더 퀄리티 있는 설명이 될거 같습니다.
  2. haemil
    감사합니다. 잘 보고 있습니다.
  3. 열정하나
    오버라이딩 잘 보았습니다.
  4. 5월 3일 오버라이딩 잘봤습니다!
  5. 감사합니다~ 잘 봤습니다~
  6. 오타
    signature 부분에 통털어서 --> 통틀어서 아닌가용..
  7. 궁금
    상위클래스를 수정하지 않고 하위클래스에서 기본적인 걸 상속받고 기능을 변경하여 사용하기 위함인데,
    상위클래스의 void를 int 로 수정해버리면 해당 상위클래스를 상속받는 하위클래스가 여러개라면 문제가 되지 않나요?
    아니면 오버라이딩의 조건인 서명이 동일해야 하는 게 우선되기 때문인건가요?
  8. 아라
    정말 잘 듣고 있습니다 이해가 잘 되네요!!
  9. super 라는 키워드가 가지는 성질을 생각한다면 될꺼 같습니다.

    해당 키워드는 코드상에서. 덮어쓸 메소드를 직접 코딩한다는 것보다 간결하고 자바에서 약속된 개념을 이용하기때문에

    코드상에서 통일된 해석을 제공하게 되어 '공통적인 코딩 방법'이라는 올바른 해결법을 만들어 주는것이라고 생각됨니다.
    대화보기
    • 강의 내용중에 return 을 만나게 되면 함수가 종료되고 값을 반환해준다고 했는데.

      return super.avg();

      아래에 재정의 하고 싶은 기능을 추가하면 된다고 말씀하셔서.

      조금 이해가 안되었지만.

      return 키워드 전에 필요한 기능을 코딩하고 기능을 덮어쓰면 된다는 의미로 받아들여도 될까요?
    • 하면된다하자
      완료.
    • 데이터정복
      오버라이딩의 기능과 조건
      부모와 자식 로직의 중복제거를 위한
      return super.avg();
      System.out.println("실행결과는"+ c1.avg());

      완료!
    • Younghun Liam Youn
      부모 클래스의 메소드가 계속해서 원래 기능을 할 수 있도록 하기 위해서는 부모 클래스에 Int 변수를 하나 선언하고 수정하려는 메소드 내에서 그 변수를 통해 값을 println하도록 수정한 후 리턴할 값으로 그 변수를 설정 해놓으면 클래스의 기능을 그대로 유지할 수 있겠군요!
    • Younghun Liam Youn
      하위 클래스에서 상위 클래스 메소드의 리턴 값만을 받으려면 결국 상위 클래스의 메소드를 수정해야하는거군요...! 그럼 애초에 하위 클래스를 사용하는 의미가 없어지는 것 아닌가요?
    • GoldPenguin
      감사합니다.
    • 완료.!
    • 부모 클래스의 메소드를 변경하지 않고 그대로 사용하면 super를 사용할 필요가 없지만 기존 기능에 다른 기능을 추가할 때 사용할 수 있겠네요.
    • 박경호
      완료 호 ㄱㅅㄱㅅ
    • seokmin2004
      완료!
    • C언어만마스터
      완료
    • shonny
      감사합니다. 말씀을 조근조근 천천히 그리고 부담없게 해주셔서 정말 편해요. ㅎㅎ
    • qkrrudtjr954
      클래스 내에 생성자가 정의되어 있지 않다면, 이는 암묵적으로 기본 생성자를 가지고 있습니다. 그러나 부모 클래스가 매개변수를 가진 생성자를 가지고 있다면, 기본 생성자를 암묵적으로 생성되지 않기 때문에 기본 생성자를 정의해주는 것입니다. 하지만 위와 같은 본문에서는 상위, 하위 클래스 모두 생성자를 정의하지 않았기 때문에 기본 생성자를 사용하여 에러가 나지 않는 것 입니다.
      대화보기
      • 미림_likelion
        수강 완료했습니다. 감사합니다.
      • 재영
        개념이 잘못되었다고 생각돼 댓글 남깁니다.
        부모클래스의 메소드를 아예 대체해버리는게 아니라 그 자식클래스 내부에서만 그 부모클래스를 무시하고
        새로 오버라이딩된 메소드를 사용하는것입니다.
        또한 그 오버라이딩 된 메소드는 그 자식클래스가 새로운 자식클래스에게 상속을 하지 않는 이상
        그 자식클래스내부에서 한정되는거기 때문에 부모클래스를 변경할 필요는 없습니다.
        대화보기
        • J_Project
          감사합니다!
        • 김인섭
          감사합니다.
        • 해결
          위의 예제에는 부모클래스와 자식클래스에 default 생성자메소드(기본 생성자메소드) 밖에 없기 때문에 오류가 발생하지 않습니다.
          대화보기
          • 궁금
            이전 상속과 생성자 강의 에서는 부모 클래스에 기본 생성자가 없으면 오류가 발생한다고 했는데, 이번 예제의 경우에는 부모클래스에 기본생성자가 존재하지 않는데 어떻게 오류가 발생하지 않는거죠??
          • 진격의숑숑
            감사합니다
          • 라떼
            감사합니다!
          • 마린조아
            실제 업무에서 어떻게 쓰일지 궁금해 지는 강의이네요.
            이 강의 자체만으로는 오버라이딩을 하기 위해서 부모클래스의 메소드를 수정해야 하는 문제가 있는데 만약에 부모클래스를 직업적으로 이용하던 클래스들은 그로 인해 생기는 연관된 문제에 대해서는 언급이 없어서요.
            오히려 그게 더 문제가 될거 같습니다. 상당히 이용에 제약이 생길 수 밖에 없는 개념이네요.
            물론 부모클래스의 수정 후 에도 별다른 문제가 없고 자식클래스에서 오버라이딩해서 super 하단 소스에 추가적인 사항을 적용해서 이용할 수 있는 상황이라면 유용해 보입니다.
          • 부모와 자녀 사이
            오버라이딩 시 부모 메소드와 자식 메소드의 시그니처가 동일해야하고, 그렇기 때문에 위 영상에서는 부모쪽 시그니처를 변경해서 문제를 해결하셨는데...

            다른 사람이 만들어놓은 클래스를 건들지 않아야 한다고 알고있습니다.
            부모 클래스 자체를 다른 사람이 만들어 놓고 내가 상속받아 오버라이딩 해야하는 상황에 위와 같은 문제발생 시
            어떻게 해결해야 할까요?
          • Weaver
            강의 감사합니다~
          • DoitDoit
            많은 도움이 되었습니다!^^
          • Jay Jaewoong Kang
            고맙습니다.
          • JustStudy
            고맙습니다
          • 김트라슈
            감사합니다
          • 이마본
            자식 클라스에서 오버라이딩을 할때 추가되는 부분이 분명 존재 할 것이고 추가되는 부분이 있다는 말은 기존의 부모 메소드의 소스내용을 그대로 적어야되는 부분이 있을텐데 만약 그부분이 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
                    좋은강의 감사합니다
                  버전 관리
                  egoing
                  현재 버전
                  선택 버전
                  graphittie 자세히 보기