Java

[Java] 상속4(Overriding)

꽃달린감나무 2022. 1. 16. 19:25
728x90
  • Overriding

상위 클래스에서 정의 된 메소드의 이름, 반환형, 매개변수 선언까지 완전히 동일한 메소들를 하위 클래스에서 재정의 하는 것을 Method Overriding 이라고 합니다.

 

  • 하위 클래스에서 오버라이딩된 상위 클래스 메소드를 호출하는 방법
class Speaker{
	
	private int volumRate;
	
	public void showCurrentState() {
		System.out.println("Volum : " + volumRate);
	}
	
	public void setVolum(int vol) {
		volumRate = vol;
	}
}

class BaseEnSpeaker extends Speaker{
	
	private int baseRate;
	
	public void showCurrentState() {
		
		//super를 통해 하위 클래스에서 오버라이딩 된 상위 클래스의 메소드를 호출할 수 있다.
		super.showCurrentState(); 
		System.out.println("Base : " + baseRate);
	}
	
	public void setBaseRate(int base) {
		
		baseRate = base;
	}
}

-> 하위 클래스에서 오버라이딩 된 상위 클래스 메소드를 호출하고 싶다면 super 키워드를 사용하여 부를 수 있습니다!

-> super.showCurrentState();

 

  • 무엇이 호출될까?
    public class Overriding {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		
    		BaseEnSpeaker bs = new BaseEnSpeaker();
    		Speaker sc = new Speaker();
    		
    		bs.setVolum(10);
    		bs.setBaseRate(20);
    		//참조변수를 이용해서, 인스턴스의 오버라이딩 메소드를 호출할 떄는 하위 클래스의 메소드가 호출된다.
    		bs.showCurrentState();
    		sc.setVolum(100);
    		sc.showCurrentState();
    	}
    
    }

-> bs.showCurrentState();를 실행하게 되면 BaseEnSpeaker 인스턴스가 가지고 있는 두 개의 showCurrentState가 존재합니다. 그러나 참조변수를 통해서 접근할 수 있는 메소드는 BaseEnSpeak에 정의된 showCurrentState 하나입니다. 따라서 상위클래스에 정의된 메소드가 하위 클래스에 정의된 메소드에 의해 가려진거진 것 입니다!

 

  • 상위 클래스의 참조변수는 하위 클래스의 인스턴스 참조할 수 있습니다!
    Speaker speaker = new BaseEnSpeaker();

다음과 같은 문장도 실행이 될까요? 네 됩니다. 

왜 가능한 걸까요??

그 이유는 JAVA의 컴파일러가 클래스를 보는 관점으로 설명할 수 있습니다.

이 전 글에서 IS-A관계에 대해 설명해드렸습니다.

 

중저음 스피커는 일종의 스피커이다 ( BaseEnSpeaker is a Speaker) 이 경우는 성립하지만, 이 역의 경우인
스피커는 일종의 중저음 스피커이다(Speaker is a BaseEnSpeaker)는 성립하지 않습니다. 이 문장이 성립되면 모든 스피커는 중저음 스피커가 되기 때문입니다.

 

이러한 관점이 바로 JAVA의 컴파일러가 클래스를 보는 관점입니다. 즉 귀 문장에서 컴파일러는 BaseEnSpeaker의 인스턴스를 Speaker의 인스턴스로 보고있는 것 입니다. 하지만 Speaker의 인스턴스를 BaseEnSpeaker로 보지는 않습니다.

 

speaker.showCurrentState() //실행 가능
speaker.setVolum(12) //실행 가능
speaker.setBaseRate(23) //실행 불가

 

showCurrentState()와 setVolum()은 실행되지만 setBaseRate는 왜 실행되지 않을까요?

위 이유와 같이 JAVA의 컴파일러가 speaker를 BaseEnSpeaker의 인스턴스로 보지 않고 Speaker의 인스턴스로 보기 때문입니다. 즉, Speaker 참조변수로 인스턴스를 참조하면 참조하는 인스턴스에 상관없이 Speaker에 정의된 메소드들만 호출이 가능합니다!!

 

  • 상속에 있어서 인스턴스 참조 관계(위 내용의 정리)

상위 클래스 참조 변수는 자신 클래스의 인스턴스와 하위 클래스의 인스턴스를 참조할 수 있습니다. 

class AAA{...}
class BBB extends AAA{...}
class CCC extends BBB{...}

AAA ref1 = new BBB(); // o
BBB ref2 = new CCC(); // o
CCC ref3 = new CCC(); // o

CCC ref1 = new ...
BBB ref2 = ref1 // o

AAA ref1 = new CCC();
BBB ref2 = ref1 // x 
//하위 클래스 참조변수는 상위 클래스의 인스턴스를 참조할 수 없다.

 

마지막 줄 BBB ref2 =ref1을 오류 없이 실행하게 할려면 형변환을 해주어야 합니다.

AAA ref1 = new CCC();
BBB ref2 = (CCC)ref1 // x

이러한 형 변환는 드물게 발생하기 때문에, 이러한 형 변환을 시도했다면, 그 코드의 구성이 올바른 건지 고민해야합니다.

 

  • 그럼 다음과 같은 상황에서 어떤 메소드가 호출 될까요?
class AAA{
	 
	public void  rideMethod() {System.out.println("'AAA' method");}
	public void  loadMethod() {System.out.println("void method");}

}

class BBB extends AAA{
	
	public void rideMethod() {System.out.println("'bbb' method");}
	public void loadMethdo(int num) {System.out.println("int method");}
}

class CCC extends BBB{
	
	public void rideMethod() {System.out.println("'ccc' method");}
	public void loadMethdo(double num) {System.out.println("double method");}
}


public class RideAndLoad {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			
		AAA ref1 = new CCC();
		BBB ref2 = new CCC();
		CCC ref3 = new CCC();
		
		ref1.rideMethod();
		ref2.rideMethod();
		ref3.rideMethod();
		
		ref1.loadMethod();
		ref2.loadMethdo(0);
		ref3.loadMethdo(3.2);
	}
}

결과 : 

'bbb' method
'ccc' method
'ccc' method
'ccc' method
void method
int method
double method

결과를 보면 참조변수의 자료형에 상관없이 마지막으로 오버라이딩을 한 메소드만 호출됩니다. 

즉, 오버라이딩이 이루어지면, 오버라이딩이 된 상위 클래스의 메소드는 어떤한 참조변수로도 호출이 불가능 하도록 가려지기 때문에, 참조변수의 자료형에 상관없이 인스턴스 외부에서는 절대 호출이 불가능하며, 대신에 오버라이딩을 한 하위 클래스의 메소드가 호출됩니다!

 

-Bonus-

오버로딩은 메소드의 이름만 같을 뿐, 매개변수의 선언이 다르기 때문에, 메소드 호출 시 전달되는 매개변수에 따라서 호출되는 메소드가 결정됩니다.

 

-요약-

 1.상위 클래스의 참조변수는 하위 클래스의 인스턴스를 참조할 수 있다.
 2.참조변수 자료형의 상관없이 마지막으로 오버라이딩을 한 메소드만 호출된다.
 3.오버라이딩 된 상위 클래스의 메소드는 오버라이딩을 한 하위 클래스의 메소드에 위해 가려진다.
 4.외부에서는 참조변수를 통해서 오버라이딩 된 상위 클래스의 메소드를 호출할 수 없다.
 5.해당 클래스의 오버라이딩 메소드를 호출할려면, 해당 클래스의 참조 변수가 해당 클래스의 인스턴스를 참조해야한다.

  • 그럼? 변수는?

예제를 통해 한 번 확인해 봅시다.

class AAA1{ public int num = 2;}
class BBB1 extends AAA1{ public int num = 5;}
class CCC1 extends BBB1{ public int num = 7;}

public class ValReDecle {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		CCC1 refa = new CCC1();
		BBB1 refb = refa;
		AAA1 refc = refb;
		
		System.out.println(refa.num);
		System.out.println(refb.num);
		System.out.println(refc.num);
	}
}

결과 : 7, 5, 2

 

1. refb는 CCC1의 인스턴스를 참조하고 있지만 참조변수의 자료형이 BBB이기 때문에 BBB class의 num에 접근한다.
2. 메소드는 참조변수 자료형에 따라 차이가 없지만 변수의 경우 참조 변수 자료형에 따라 접근 가능한 인스턴스 변수에 차이가 있다.

728x90