본문 바로가기
JAVA

[JAVA] 다형성, 참조변수의 형변환(업캐스팅, 다운캐스팅)

by 정공자씨 2024. 4. 19.

 

다형성

개념

  • 하나의 객체가 여러 타입의 객체를 가질 수 있음
    • 즉, 클래스의 상속 관계에서
    • 부모 클래스 타입의 참조 변수로 자식 클래스의 인스턴스(객체)를 참조할 수 있음
1. 부모 클래스의 참조변수가 부모 클래스 본인의 객체뿐만 아니라, 자식 클래스의 인스턴스(객체)도 참조할 수 있음
2. 자식 클래스의 인스턴스(객체)를 자식 클래스 타입의 참조 변수 뿐만 아니라, 부모 클래스 타입의 참조 변수로 참조할 수 있음

 


 참조변수의 다형성

  • 클래스들 사이에서 반드시 상속 관계가 전제할 것
  • 다형성을 위하여 부모 클래스 타입의 참조변수로 자식 클래스 타입의 인스턴스(객체)를 참조할 수 있도록 함 
  • 반대의 경우, 자식 클래스 타입의 참조 변수로는 부모 클래스 타입의 인스턴스를 참조할 수 없음
    • 그 이유는, 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버(부모 클래스 인스턴스)보다 많기 때문
    • 자식 클래스는 부모의 멤버를 상속 받기 때문에, 사용할 수 있는 멤버의 개수가 부모 클래스보다 같거나 더 많음
class Parent { }
class Child extends Parent { }
public class Main {
    public static void main(String [] args) {
    	
        Parent pa = new Parent();  // 허용
        Child ch = new Child();    // 허용
        
        // 다형성 이용
        Parent pa2 = new Child();  // 허용
        
        // 자식 타입의 참조변수로, 부모 클래스 타입의 인스턴스 참조 X
        Child ch2 = new Parant();  // 오류 발생, (X)
    }
}

 

 

예제

class Animal {
    String category;
}
class Cat extends Animal { // Animal 클래스(상위 클래스)를 상속
    String name;
    int age;
}
public class AnimalTest {
    public static void main(String [] args) {
    	// 상위 클래스 타입의 참조변수(a)가 그 상위 클래스의 객체를 가리킴
    	Animal a = new Animal(); 
        
        // 다형성: 상위 클래스 타입의 참조변수(b)가 하위 클래스의 객체를 가리킬 수 있음       
        Animal b = (Animal)new Cat(); 
        Animal b = new Cat(); // (형변환타입) 생략 가능
    }
}

 

 


 
 

자바의 참조형 캐스팅(형 변환)

캐스팅(casting) 
타입을 변환하는 것, 형 변환

 
상속 관계의 클래스 간에 형 변환하기

  • 상속 관계의 클래스는 부모 클래스와 자식 클래스로 구분
  • 자바의 상속 관계에 있는 부모와 자식 클래스 간에는만 서로 형 변환이 가능함
    • 기본형 타입들끼리 서로 형 변환이 가능한 것과 같이
    • 참조형 타입들끼리도 형 변환이 가능
  • 자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은, 형 변환 생략 가능(업캐스팅)
  • 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은, 반드시 명시(다운캐스팅)
클래스는 참조형 타입(reference 타입)이므로, 부모클래스와 자식 클래스 간의 형 변환을 '참조형 캐스팅'이라고 함

 
 

참조형 캐스팅의 종류

  1. 업캐스팅
  2. 다운 캐스팅

  • 자식 클래스의 객체는 부모 클래스를 상속하고 있기 때문에, 자신의 멤버는 물론이고 부모의 멤버(변수, 메서드 등)도 모두 사용할 수 있음
  • 반면 부모 클래스의 객체는 자식 클래스의 멤버(변수, 메서드 등)를 갖지 않으므로, 사용할 수 없음

 
 



업캐스팅(upcasting)

개념

  • 클래스의 상속 구조에서
  • 부모 클래스 타입의 참조 변수가 자식 클래스 인스턴스를 가리킬 수 있는 능력
  • 즉, 자식 클래스 타입이 부모 클래스 타입으로 변환되는 것(타입의 변환)

 

특징

  • 자식 클래스 타입을 부모 클래스 타입으로 변환된다는(업캐스팅) 것은, 멤버의 개수가 감소한다는 것
    • 즉, 자식 클래스에만 있는 변수와 메서드는 실행하지 못한다는 것
    • = 부모 클래스 타입으로 타입이 변환되었기 때문에 자식 클래스의 멤버 사용 불가
    • 부모 타입의 참조 변수로 부모 클래스에 있는 변수와 메서드만 접근할 수 있음
  • 업캐스팅을 하고 나서, 메서드를 호출할 때
    • 만일 자식 클래스에서 부모 클래스의 메서드를 오버라이딩 한 메서드(@override)가 있는 경우
    • 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행

 

업캐스팅을 하는 이유가 뭘까?

  • 공통적으로 사용하려고 하는 부분을 만들어서 관리하기 위함
  • 즉 상속 관계에서 자식 클래스의 개수에 상관 없이, 하나의 인스턴스(부모 클래스 하나)로 묶어서 관리할 수 있음

상속 관계를 맺어 부모 클래스 타입으로 업캐스팅을 하면, 하나의 타입(부모 타입)으로 관리할 수 있음

 

 

업캐스팅 형식

// () 괄호에 변환할 타입을 작성하여 형 변환
Animal a = (Animal) new Cat();	// Animal타입으로 형 변환

// 업캐스팅은 자동 형변환 가능, () 괄호 생략 가능
Animal a = new Cat();
  • Cat 클래스는 Anumal클래스를 상속받은 클래스이므로
    • Cat은 Cat 타입이면서 동시에 Animal 타입이기도 함
  • 따라서, Cat 클래로 인스턴스를 생성할 때, 이 인스턴스의 타입을 Animal형으로 형 변환을 할 수 있음
    • 원래의 Cat 인스턴스의 타입은 Cat인데
    • new Cat을  Animal형으로 변경할 수 있음. 즉, (Animal) new Cat;
  • 참조변수 a가 가리키는 것은
    • 참조 변수 클래스형에 기반한 멤버변수와 메서드에 접근할 수 있음
    • 즉, Animal 클래스의 멤버변수와 메서드만 접근 가능

 

 

업캐스팅 시에 주의할 점

  • 사용할 수 있는 멤버(멤버변수, 메서드)의 개수가 감소하여, 멤버에 대한 접근이 제한됨
자식 클래스 타입을 부모 클래스 타입으로 형 변환을 하는 것이기 때문에, 자식 타입을 마음대로 사용할 수 있을 것 같지만, 업캐스팅된 상태에서 자식 타입에 마음대로 접근할 수 없음

[ 이유 ]
부모를 상속하여 부모의 멤버(멤버변수, 메서드)는 물론 본인의 멤버까지 사용할 수 있었던 자식 클래스에서, 부모 클래스로 업캐스팅(형변환)을 하였으니 당연히 멤버 개수가 감소하게 됨. 즉, 부모 클래스로 형 변환을 하였으니, 부모의 멤버(멤버변수, 메서드)만 사용할 수 있게 됨

[ 원칙 ]
업캐스팅이 된 상태에서 부모 클래스 타입의 참조변수로, 자식 클래스의 필드나 메서드에 접근할 수 없음

[ 다운캐스팅 ]
자식 클래스의 필드나 메서드에 접근하기 위해서는, 다시 자식 클래스 타입으로 다운캐스팅 필요

 

 

업캐스팅 시에 자식 클래스의 멤버에 대한 접근이 제한

1. 업캐스팅이 된 상태에서 메서드 호출 시에, 부모 클래스에서 오버라이딩 된 메서드를 먼저 호출

부모 클래스에 bark() 메서드가 있고, 자식 클래스에서 오버라이딩 된 메서드(같은 메서드명을 가지는 bark() 메서드)가 있을 때, 업캐스팅이 된 상태에서 bark() 메서드를 호출하면 부모와 자식 클래스의 bark 메서드 중에 어떤 메서드가 호출이 될까?

✔️ 원칙적으로는, 부모 클래스 타입의 참조변수로는 자식 클래스의 메서드를 호출할 수 없음️
✔️ 하지만 업캐스팅 된 상태에서는, 하위 클래스에서 오버라이딩 된 메서드가 먼저 호출이 됨

 

2. 업캐스팅이 된 상태에서 멤버변수를 호출하면, 부모 클래스의 멤버변수가 호출됨

  • 부모 클래스 타입의 참조변수로는, 자식 클래스의 멤버변수나 메서드를 접근할 수 없음
  • 부모 클래스 타입의 참조변수로는 부모 클래스의 멤버변수에 접근할 수 있음

3. 예제

class Animal {
	String category;
    
	void bark() {
		System.out.println(“동물이 짖는다”);
	}
}

class Cat extends Animal {
	@override
	void bark() {
		System.out.println(“고양이가 짖는다 : 니야옹”);
	}
}
  • 부모 클래스(Animal)의 bark() 메서드를
    • 자식 클래스(Cat)에서 오버라이드(@override)하여 메서드를 재정의 함
    • 따라서, a.bark() 메서드 호출 시에, 자식 클래스에서 오버라이딩 된 메서드가 호출
  • 부모 클래스 타입의 참조변수(a)로
    • 자식 클래스의 멤버변수나 메서드를 호출할 수 없음
[주의할 점]
상위 클래스 타입의 참조변수(a)로, 하위 클래스의 멤버변수나 메서드를를 호출할 수 없음

 
 

 

 

 

다운캐스팅(down-casting)

[ 자식 클래스에만 있는 고유한 메서드나 멤버변수를 실행하려면 어떻게 해야 할까? ]

오버라이딩 한 메서드가 아닌 이상은, 업캐스팅한 부모 클래스 타입으로 자식 클래스의 고유한 메서드를 실행할 수 없음

따라서, 업캐스팅한 객체를 다시 자식 클래스 타입으로 다시 형을 변환해야 함.  즉 다운 캐스팅이 필요

개념

  • 다운캐스팅은 부모 클래스 타입이 자식 클래스 타입으로 형변환(캐스팅)되는 것
  • 부모 클래스 타입으로 업캐스팅한 객체다시 자식 클래스 타입의 객체로 되돌리는 것

 

 

다운 캐스팅의 목적

  • 부모 클래스로 업캐스팅된 자식 클래스를 다시 복구하여, 자식 클래스 본래의 멤버변수(필드)와 메서드를 사용
  • 즉, 자식 클래스 본래의 기능을 사용하기 위하여 다운캐스팅을 함

 

 

다운캐스팅 형식

  • 형 변환 연산자를 반드시 사용(생략 불가)
// [업캐스팅]
Animal a = new cat();	// (업캐스팅)형 변환 연산자 생략 가능

// [다운 캐스팅] 
Cat c = (Cat)a;	// (다운캐스팅)형 변환 연산자 생략 X

 

형 변환 연산자를 반드시 써줘야 하는 이유

  • 다운 캐스팅을 하면, 다시 사용할 수 있는 멤버(멤버변수, 메서드)의 개수가 증가하는 것이므로
  • 참조변수가 실제로 가리키는 객체가 무엇인지를 알려줘야 함
  • 괄호 안에 사용할 타입을 적어주어, 어떤 클래스의 멤버가 증가하는지를 알려줘야 함

 

 

예제

class Animal {
	String category;
    
	void bark() {
		System.out.println(“동물이 짖는다”);
	}
}
class Cat extends Animal {
    String name;
    int age;

    @Override
    void bark() {
        System.out.println(“고양이가 짖는다 : 니야옹”);
    }

    void eating() {
        System.out.println(“고양이의 츄르 먹기”);
    }
}
public class AnimalTest {
    public static void main(String [] args) {
    	// [업캐스팅]
        Animal a = new Cat();  // Animal(부모클래스)타입으로 형 변환
        
        // [다운캐스팅]
        Cat c = (Cat)a;  // 다운캐스팅은, 형변환 연산자 생략 불가

        c.eating();  // 출력: "츄르 먹기"
        c.bark();  // 출력: "고양이 니야옹"
        
        c.name = "냐옹이";  // 자식클래스의 멤버변수 접근
        System.out.println(c.name);  // 출력: "냐옹이"
    }
}

 

 

 

다운 캐스팅을 할 때 주의사항

  • 업캐스팅한 객체본래의 형태로 되돌리는 용도로 사용하여야 함
  • 다운 캐스팅 할 객체업캐스팅된 부모 클래스의 객체여야 함
    • 부모 클래스의 객체(업캐스팅이 된 적 없는)를 다운 캐스팅 하면
    • 컴파일 시점에는 오류가 없지만 런타임시 오류를 발생시킴

 

1. 부모 클래스 객체(업캐스팅 된 적 없음)를 다운 캐스팅할 때 : 에러 발생

  • 컴파일(저장) 시점에는 에러가 없지만
  • 실행(runtime) 시점에는 형 변환 오류가 발생함


 2. 업캐스팅이 된 객체를 대상으로 다운 캐스팅 할 때

 

 

 

 

 

 

 

✔ instanceof 연산자

개념

  • 어떤 객체의 참조변수어떤 클래스의 타입인지를 판별하여 true, false를 반환해줌
  • 객체에 대한 클래스 타입(참조형 타입)인 경우에만 사용이 가능
[ instanceof 연산자 ]
기본형 타입(int, long 등)에는 사용이 불가능

 

 

사용하는 경우

  • 다형성으로 인해 런타임 시에 참조 변수가 실제로 참조하고 있는 인스턴스의 타입을 확인할 필요가 있음

 

문법

전달된 참조변수 instanceof 클래스이름

 

 

예제

class Animal {
    String category;

    void bark() {
        System.out.println("동물이 짖는다");
    }
}

class Cat extends Animal {
    String name;
    int age;

    @Override
    void bark() {
        System.out.println("고양이 니야옹");
    }

    void eating() {
        System.out.println("고양이의 츄르 먹기");
    }
}

class Dog extends Animal {
    String name;
    int age;

    @Override
    void bark() {
        System.out.println("강아지 멍멍");
    }

    void playing() {
        System.out.println("강아지의 한강 산책");
    }
}

 

 

1. [ 실행 결과 ] 업캐스팅 가능

public class AnimalTest {
    public static void main(String [] args) {
          
    	// [업캐스팅이 되었는지를 판단]
        Cat cat = new Cat(); // 같은 클래스 타입으로 객체 생성
        
        // 객체의 참조변수(cat)가 어떤 클래스 타입인지를 판별
        if(cat instanceof Animal) {  // 참조변수(cat)이 Animal 클래스 타입이면 업캐스팅
            System.out.println("업캐스팅 가능"); 
            Animal a = cat; // 업캐스팅
        } else {
            System.out.println("업캐스팅 불가능");
        }
    }
}

 

2. [ 실행 결과 ]  강아지의 한강 산책

public class AnimalTest {
    public static void main(String [] args) {
          
        Animal animal = new Dog();  // [업캐스팅]

        if(animal instanceof Dog) { // Dog클래스 타입이라면, Dog에 있는 메서드를 호출
            ( (Dog)animal ).playing();
        } 
        else {
            System.out.println("나는 Animal 클래스 타입");
        }
    }
}

 

3. [ 실행 결과 ]  고양이의 츄르 먹기

public class AnimalTest {
    public static void main(String [] args) {
          
        Animal ani= new Cat();  // [업캐스팅]

        if(ani instanceof Dog) { // 참조변수가 Dog클래스 타입이라면, Dog에 있는 메서드를 호출
        	((Dog)ani).playing();
        } 
        else if(ani instanceof Cat) { // 참조변수가 Cat클래스 타입이라면, Cat에 있는 메서드를 호출
        	((Cat)ani).eating();
        }
        else {
        	System.out.println("나는 Animal 클래스 타입");
        }
    }
}

 

 

4. [ 실행 결과 ]  나는 Animal 클래스 타입

public class AnimalTest {
    public static void main(String [] args) {
          
        Animal ani= new Animal();
        
        if(ani instanceof Dog) {
        	((Dog)ani).playing();
        } 
        else if(ani instanceof Cat) {
        	((Cat)ani).eating();
        }
        else { // 참조변수(ani)가 Dog클래스타입, Cat클래스타입도 아닌 경우
        	System.out.println("나는 Animal 클래스 타입");
        }
    }
}

 

 

 

 

 

 

 

 

출처

 

 

☕ JAVA 업캐스팅 & 다운캐스팅 - 완벽 이해하기

자바의 참조형 캐스팅 하나의 데이터 타입을 다른 타입으로 바꾸는 것을 타입 변환 혹은 형변환(캐스팅) 이라고 한다. 자바의 데이터형을 알아보면 크게 두가지로 나뉘게 된다. 기본형(primitive ty

inpa.tistory.com

 

[JAVA] 상속(extends) 개념, 상속 클래스 생성과 형 변환

상속의 개념, 생성자 호출(super), 형 변환

velog.io