[자바] 레퍼런스 형변환(업캐스팅, 다운캐스팅)

Gammi·2022년 10월 25일
0

JAVA

목록 보기
19/35
post-thumbnail

📚 레퍼런스 형변환


  • 어떤 객체(인스턴스)를 다른 타입으로 변환

  • 참조형 데이터타입간의 형변환


  • 반드시 상속 관계가 전제되어야 함

    (상속 관계가 아닌 타입끼리의 형변환X)

  • 슈퍼클래스 타입 <-> 서브클래스 타입


  • 묵시형 형변환(업캐스팅, Up Casting)

  • 명시적 형변환(다운캐스팅, Down Casting)





📕 업캐스팅


  • 슈퍼클래스 타입 레퍼런스로 서브클래스 인스턴스를 참조

    = 서브클래스의 인스턴스를 슈퍼클래스 타입으로 변환

  • 컴파일러에 의해 자동 형변환 일어남(=묵시적 형변환)

  • 참조 가능 영역이 축소

    (슈퍼클래스로부터 상속된 멤버만 접근 가능해짐)

  • 서브클래스 인스턴스 멤버 중 공통 항목을 제외한 나머지에 대한 포기 선언을 하는 것

    -> 대신 하나의 슈퍼클래스 타입으로 여러 서브클래스의 인스턴스 참조 가능


public static void main(String[] args) {
  Parent p = new Parent(); // 슈퍼클래스 인스턴스 생성
  p.parentPrn();
  // parent 타입으로 접근 가능한 메서드는
  // 슈퍼클래스의 메서드 하나!
  
  Child c = new Child(); // 서브클래스 인스턴스 생성
  c.parentPrn();
  c.childPrn();
  // child 타입으로 접근 가능한 메서드는
  // 슈퍼클래스의 메서드와 서브클래스의 메서드 두 개!
  
  p = c; // 업캐스팅 방법 1
  Parent p = new Child(); // 업캐스팅 방법 2
  // 이렇게 하려면 위에서 parent는 인스턴스 생성하면 안됨!
  
  p.parentPrn();
  // 업캐스팅 후에는 상속된 메서드만 접근 가능
  // 서브클래스의 메서드는 안 보임
}

class Parent {
  public void parentPrn() {
    System.out.println("슈퍼클래스의 parentPrn()");
  }
}

class Child extends Parent {
  public void childPrn() {
    System.out.println("서브클래스의 childPrn()");
  }
}

🔗 참고

업캐스팅 방법 1과 방법 2는 똑같을까?

X

  • p == c로 인스턴스를 비교하면 방법 1은 true가 출력되고 방법 2는 false가 출력된다
// 방법 1
p = c;
// c의 주소값을 p에 넣는 거라서 인스턴스가 같음
// 방법 2
parent p = new Child();
// 새로운 인스턴스를 생성하는 것!
// new가 붙었으니까!
// 그래서 처음에 생성된 참조변수 c랑은 다른 인스턴스!





📕 다운캐스팅


  • 서브클래스의 레퍼런스가 슈퍼클래스의 인스턴스를 참조

    = 슈퍼클래스의 인스턴스를 서브클래스 타입으로 형변환

  • 참조 가능한 영역이 확대

  • 자동 형변환이 일어나지X (강제 형변환 필요)

  • 강제 형변환을 통해 구문 오류가 해결되더라도 실제 실행 시점에서 오류(실행 오류) 발생하게 됨

    -> 존재하지 않은 영역의 참조 위험성 때문

    => 자바에서는 다운캐스팅 지원X


Parent p = new Parent();
p.parentPrn();

Child c; // 서브클래스 Child 타입 변수 선언

c = p; // 다운캐스팅, !!!컴파일 에러!!!
// 자동형변환 안되기 때문에 오류 발생함

c = (Child)p; // 강제형변환으로 가능
c.parentPrn(); // 원래 가지고 있던 메서드
c.childPrn(); // parent는 없던 메서드
// 존재하지 않는 영역에 대한 참조 위험성 때문에
// 자바에서 다운캐스팅 존재하지 않음!!!

다운 캐스팅이 가능한 경우

  • 이미 업캐스팅된 인스턴스를 다시 다운캐스팅 하는 경우

Parent p = new Parent();
p = new Child(); // 업캐스팅
p.parentPrn(); // 접근 가능한 메서드는 하나!

Child c = (Child)p; // 다운캐스팅
// 자동 형변환 안되기 때문에 강제형변환 수행

c.parentPrn(); 
// parent가 원래 가지고 있었던 메서드
// 다운캐스팅할 때 같이 가져옴
c.childPrn();
// Child가 원래 가지고 있었던 메서드라서 존재하는 영역임!





📑 instanceof 연산자


  • 좌변의 객체가 우변 클래스 타입인지 판별

  • 참조변수가 부모객체를 참조하는지 자식객체를 참조하는지 판별 가능

  • 판별 결과가 true면 형변환 가능

  • 판별 결과가 false면 형변환 불가능

  • 우변의 클래스 타입과 같거나 우변 클래스 타입의 자식 타입 모두 가능

  • A is a B = A instance of B

  • 판별 결과를 boolean 타입 변수에 저장하거나 if문에 판별하는 문장 직접 사용 가능


< 기본 문법 >

if(A instanc of B) { // A는 참조변수 B는 클래스명
 // 형변환 가능
} else {
  // 형변환 불가능
}

public static void main(String[] args) {
  Smartphone sp = new Smartphone("아이폰14", "010-1234-5678", "애플");
  sp.call();
  sp.sms();
  sp.kakaoTalk();
  // 상속 받았기 때문에 슈퍼클래스의 메서드까지 호출 가능
  
  if(sp instanceof Handphone) {
    System.out.println("sp는 Handphone이다");
    Handphone p = sp; // 업캐스팅
    p.call();
    p.sms();
    // 업캐스팅하면 공통된 메서드만 사용 가능하기 때문에
    // 더 이상 카카오톡은 이용할 수 없음
  } else {
    System.out.println("sp는 Handphone이 아니다")
    System.out.println("형변환 불가!")
  }
  
  Handphone hp = new Smartphone("아이폰14", "010-1234-5678", "애플"); // 업캐스팅
  
  if(hp instance of Smartphone) {
    System.out.println("hp는 smartphone이다");
    // 다운캐스팅 가능
    Smartphone sp2 = (Smartphone)hp;
    // 대신 자동 형변환이 안되기 때문에 강제형변환 필요!
    sp2.call();
    sp2.sms();
    sp2.kakaoTalk();
    // 다운캐스팅 해서 다시 3개의 메서드 모두 사용 가능
  } else {
    System.out.println("hp는 smartphone이 아니다");
    System.out.println("형변환 불가능");
  }
}

class Handphone {
  String modelName;
  String number;
  
  public Handphone(String modelName, String number) {
    super();
    this.modelName = modelName;
    this.number = number;
  }
  
  public void call() {
    System.out.println("전화 가능");
  }
  public void sms() {
    System.out.println("문자 가능");
  }
}

class Smartphone extends Handphone {
  String osName;
  
  public Smart(String modelName, String number, String osName) {
		super(modelName, number);
		this.osName = osName;
	}
    
    public void kakaoTalk() {
    System.out.println("카톡 가능");
  }
}





📗 동적 바인딩


  • 상속 관계에서 업캐스팅 후 메서드 호출 시 컴파일 단계에서 실행 대상과 실제 실행 단계에서의 실행 대상이 달라진 것

  • 참조 변수 타입과 무관하게 실제 인스턴스의 메서드를 실행하게 됨


⭐ 입력

public static void main(String[] args) {
  Parent p = new Parent();
  p.parentPrn();
  
  Child c = new Child();
  c.childPrn();
  c.parentPrn();
  
  System.out.println("====================================");
  
  p = c; // 업캐스팅
  p.parentPrn();
  // 업캐스팅 했기 때문에 더이상 childPrn() 호출 불가
}

class Parent {
  public void parentPrn() {
    System.out.println("슈퍼클래스의 parentPrn()");
  }
}

class Child extends Parent {
  public void childPrn() {
    System.out.println("서브클래스의 childPrn()");
  }
  
  public void parentPrn() { // 오버라이딩 했음
    System.out.println("서브클래스에서 오버라이딩된 parentPrn()");
  }
}

📌 출력

슈퍼클래스의 parentPrn()
서브클래스의 childPrn()
서브클래스에서 오버라이딩된 parentPrn()
====================================
서브클래스에서 오버라이딩된 parentPrn()
  • 업캐스팅 후 parentPrn() 메서드를 호출하면 Child 클래스에서 오버라이딩된 parentPrn() 메서드가 호출됨

  • 메서드 코드를 작성하는 시점(컴파일 시점)에는 Parent 클래스의 parentPrn() 메서드를 호출하는 코드이지만 실행 시점에는 Child 클래스에서 오버라이딩된 parentPrn() 메서드가 호출됨

    => 컴파일 단계에서의 실행 대상과 실행 단계에서 실행 대상이 다름

profile
개발자가 되었어요⭐️

0개의 댓글