다형성(polymorphism) - 업캐스팅과 다운캐스팅

Bloooooooooooooog..·2022년 12월 23일

다형성이란

다형성은 객체 지향 언어의 특징 중 하나로, 하나의 객체를 다양한 형태로 이용할 수 있는 특징이다. 참조 변수 하나로 상속 관계에 있는 여러 타입 객체를 참조할 수 있다.

  • 다형성은 객체지향의 또 다른 특성인 상속과 관련이 많다. 👨🏻‍👦

클래스의 형변환

1. 업캐스팅

자식 클래스의 객체를 부모 타입으로 형변환하는 것을 의미한다. 자식 클래스의 객체를 통해 부모 클래스처럼 사용할 수 있다.
상속 관계에서는 부모 타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있다.

public class JazzBand{
	private int numberOfMember;
    private String bandName;
    private String leaderPosition;
    
    JazzBand(){} //기본생성자
    JazzBand(int numberOfMember, String bandName, 
    String leaderPosition){
   		this.numberOfMember = numberOfMember;
    	this.bandName = bandName;
    	this.leaderPosition = leaderPosition;
    }//매개변수 생성자
    
    @Override
    public String toString(){
    	return numberOfMember + ", " + bandName + ", " + 
    	leaderPosition;
    }
    
    =====================================================
 
}

public class BigBand extends JazzBand{
	
    private String conductor;
    
    BigBand(){}
    
    BigBand(int numberOfMember, String bandName, 
    String leaderPosition, String conductor){
    	super(numberOfMember, bandName, leaderPosition);
    	this.conductor = conductor;
        
    @Override
    public String toString(){
    	return super.toString + ", " + conductor;
    }
    
    }
    
    ====================================================
}

public class FusionBand extends JazzBand{
	
    private int numberOfElectricInst;
    
    FusionBand(){}
    
    FusionBand(int numberOfMember, String bandName, 
    String leaderPosition, int numberOfElectricInst){
    	super(numberOfMember, bandName, leaderPosition);
   		this.numberOfElectricInst = numberOfElectricInst;
    }
    
    @Override
    public String toString(){
    	return super.toString + ", " + numberOfElectricInst;
    

}

// getter / setter는 생략한다. 
//(존재한다고 가정)

위와 같이 부모 클래스와 이를 상속받는 두 개의 자식 클래스를 만들어서 사용할 것이다.

 JazzBand jb1 = new JazzBand();
 JazzBand jb2 = new BigBand(); //업캐스팅
 JazzBand jb3 = new FusionBand(); //업캐스팅
 BigBand bb1 = new BigBand();
 FusionBand fb1 = new FusionBand();
  • 부모타입의 참조변수 = 자식 객체 == (업캐스팅)

위와 같이이 업캐스팅된 사례와 일반적으로 변수를 생성한 경우가 있다고 했을 때,

자식 객체가 부모 객체로 변한 것이므로, 자식 객체 고유의 필드와 메소드를 사용할 수 없다.

jb2.setConductor("MrNam"); // 실행 불가 
bb1.setConductor("MrNam"); // 실행 가능

만약 업캐스팅된 변수에서 자식 객체 고유의 필드나 메소드에 접근하기 위해서는 다운캐스팅이 필요하다.

2. 다운캐스팅

부모타입의 참조변수가 자식 객체를 참조하는 업캐스팅 상태에서, 만약 참조변수가 본래 자식 객체의 필드와 메소드에 접근하려고 할 때, "강제형변환" 을 통해서 본래의 필드와 메소드를 사용하는 방법

BigBand bb2 = (BigBand)jb2;
//얕은 복사를 통해 다운캐스팅해주었다.
// * 효율적인 다운캐스팅 방법!
bb2.setConductor("Scotfield");

System.out.println(((BigBand)jb2).getConductor()); 
//다운캐스팅을 이용해 자식 객체의 메소드를 호출

//주의!
//"." 연산자가 형변환 연산자보다 우선순위가 높음 

주의사항

다운 캐스팅을 할 때 자식 객체끼리 강제형변환을 하는 등 잘못된 방법으로 수행 시 java.lang.ClassCastException에러가 발생하게 된다.
이를 방지하기 위해 instanceof 연산자를 사용한다.

instanceof 연산자

참조형 변수가 어떤 클래스를 참조하는지 확인하는 연산자로,
참조하는 타입이 맞으면 true, 아니면 false를 반환한다.

JazzBand jb1 = new Bigband();
		
		if(jb1 instanceof FusionBand) {
			FusionBand fb1 = (FusionBand)jb1; // 맞을 시 다운캐스팅
			System.out.println("성공");
		}else {
			System.out.println("실패(FusionBand 타입이 아님)");
		}

위와 같이 instanceof 연산자를 사용해서 참조타입이 맞는지 확인 후 다운캐스팅을 해준다.

바인딩

바인딩은 실제 실행할 메소드 코드와 호출하는 코드를 연결하는 것을 의미한다.

1. 정적 바인딩

프로그램 실행 전 컴파일 단계에서 메소드와 메소드 호출부를 연결하는 것이다.

JazzBand jb1= new JazzBand(3, "킴스트리오", "베이스");

jb1.getBandName();

위와 같이 실행했을 때 컴파일러는 getBandName()가 JazzBand 클래스 내의 메소드임을 인지하고 프로젝트의 패키지 내 클래스에서 그 값을 가져온다. 이를 정적 바인딩이라고 한다.

2. 동적 바인딩

컴파일 시 정적 바인딩된 메소드를 실행할 당시의 객체 타입 기준으로 바인딩 되는 것이다.

JazzBand jb1 = new FusionBand(5, "텀블러즈", "키보드", 4);

jb1.toString();

jb1은 FusionBand으로 업스캐딩된 상태이며, 따라서 부모 객체의 toString() 메소드가 실행될 것 같지만, 실제로는 자식 객체에 오버라이딩된 메소드가 우선적으로 수행된다.

5, 텀블러즈, 키보드, 4

동적 바인딩에 의해 이와 같이 실행된다.

profile
공부와 일상

0개의 댓글