[Java 개념노트 33] 참조변수의 형변환과 instanceof 이해하기
안녕하세요. Java 개념노트 시리즈 서른세 번째 글입니다.
지난 글에서는 부모 타입으로 여러 자식 객체를 다룰 수 있는 다형성에 대해 정리했습니다. 이번 글에서는 다형성과 함께 꼭 알아야 하는 참조변수의 형변환과 instanceof에 대해 알아보겠습니다.
기본 자료형에서 int를 double로 바꾸거나, double을 int로 바꾸는 형변환을 배웠습니다. 객체도 상속 관계에서는 타입을 바꿔서 참조할 수 있습니다. 이때 등장하는 개념이 업캐스팅과 다운캐스팅입니다.
1. 참조변수의 형변환이란?
참조변수의 형변환은 객체를 가리키는 변수의 타입을 다른 타입으로 바꾸는 것을 말합니다.
단, 아무 타입으로나 바꿀 수 있는 것은 아닙니다. 참조변수의 형변환은 주로 상속 관계에 있는 클래스 사이에서 가능합니다.
쉽게 말하면
참조변수의 형변환은 자식 객체를 부모 타입으로 보거나, 부모 타입으로 보던 객체를 다시 자식 타입으로 보는 것입니다.
2. 기본 자료형 형변환과 참조변수 형변환의 차이
기본 자료형 형변환은 값 자체의 형태가 바뀝니다. 하지만 참조변수의 형변환은 객체 자체가 바뀌는 것이 아니라, 그 객체를 바라보는 변수의 타입이 바뀌는 것입니다.
| 구분 | 의미 | 예시 |
| 기본 자료형 형변환 | 값의 자료형을 바꿈 | double d = 10; |
| 참조변수 형변환 | 객체를 바라보는 참조 타입을 바꿈 | Animal a = new Dog(); |
3. 업캐스팅이란?
업캐스팅은 자식 타입의 객체를 부모 타입으로 참조하는 것입니다.
Dog dog = new Dog();
Animal animal = dog;
Dog는 Animal의 자식 클래스라고 가정합니다. Dog 객체는 Animal의 한 종류이므로 Animal 타입 변수에 저장할 수 있습니다.
Animal animal = new Dog();
이렇게 한 줄로 작성하는 경우도 많습니다.
4. 업캐스팅은 자동으로 된다
업캐스팅은 자바가 자동으로 처리해줍니다. 자식 객체를 부모 타입으로 보는 것은 안전하기 때문입니다.
class Animal {
}
class Dog extends Animal {
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
Animal animal = dog;
}
}
Dog는 Animal의 한 종류입니다. 그래서 Dog 객체를 Animal 타입 변수에 넣을 수 있습니다.
핵심 포인트
업캐스팅은 자식 객체를 부모 타입으로 참조하는 것이며, 자동으로 처리됩니다.
5. 업캐스팅 후 호출 가능한 메서드
업캐스팅을 하면 변수 타입은 부모 타입이 됩니다. 따라서 부모 클래스에 정의된 메서드만 바로 호출할 수 있습니다.
class Animal {
void sound() {
System.out.println("동물 소리");
}
}
class Dog extends Animal {
void bark() {
System.out.println("멍멍 짖기");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.sound();
// animal.bark(); // 오류 발생
}
}
animal 변수의 타입은 Animal입니다. 실제 객체는 Dog이지만, 변수 타입 기준으로는 bark()를 알 수 없습니다. 그래서 animal.bark()는 바로 호출할 수 없습니다.
6. 업캐스팅 후 오버라이딩 메서드 실행
업캐스팅 후 부모 타입 변수로 메서드를 호출해도, 오버라이딩된 메서드는 실제 객체 기준으로 실행됩니다.
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.sound();
}
}
class Animal {
void sound() {
System.out.println("동물 소리");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("멍멍");
}
}
실행 결과는 다음과 같습니다.
멍멍
변수 타입은 Animal이지만 실제 객체는 Dog입니다. 따라서 Dog에서 오버라이딩한 sound()가 실행됩니다.
7. 다운캐스팅이란?
다운캐스팅은 부모 타입으로 참조하던 객체를 다시 자식 타입으로 변환하는 것입니다.
Animal animal = new Dog();
Dog dog = (Dog) animal;
animal 변수는 Animal 타입이지만 실제 객체는 Dog입니다. 그래서 (Dog)를 사용해 다시 Dog 타입으로 변환할 수 있습니다.
8. 다운캐스팅은 직접 명시해야 한다
다운캐스팅은 자동으로 되지 않습니다. 개발자가 직접 괄호 안에 변환할 타입을 작성해야 합니다.
Animal animal = new Dog();
Dog dog = (Dog) animal;
dog.bark();
다운캐스팅을 하면 Dog 타입 변수로 다시 사용할 수 있기 때문에 Dog만 가진 bark() 메서드를 호출할 수 있습니다.
class Animal {
void sound() {
System.out.println("동물 소리");
}
}
class Dog extends Animal {
void bark() {
System.out.println("멍멍 짖기");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
Dog dog = (Dog) animal;
dog.bark();
}
}
실행 결과는 다음과 같습니다.
멍멍 짖기
9. 잘못된 다운캐스팅
다운캐스팅은 실제 객체 타입이 맞을 때만 안전합니다. 실제 객체 타입이 다르면 실행 중 오류가 발생할 수 있습니다.
Animal animal = new Cat();
Dog dog = (Dog) animal;
위 코드는 위험합니다. animal 변수의 실제 객체는 Cat입니다. 그런데 Dog로 변환하려고 했기 때문에 문제가 발생할 수 있습니다.
이런 경우 실행 중에 ClassCastException이 발생할 수 있습니다.
주의
다운캐스팅은 컴파일은 될 수 있어도, 실제 객체 타입이 맞지 않으면 실행 중 오류가 발생할 수 있습니다.
10. ClassCastException이란?
ClassCastException은 잘못된 타입으로 객체를 형변환하려고 할 때 발생하는 실행 오류입니다.
Animal animal = new Cat();
Dog dog = (Dog) animal; // ClassCastException 발생 가능
변수 타입만 보면 Animal이므로 Dog로 변환할 수 있을 것처럼 보일 수 있습니다. 하지만 실제 객체는 Cat입니다. Cat 객체를 Dog로 바꿀 수는 없습니다.
Animal animal = new Cat();
↓
실제 객체는 Cat
↓
Dog로 다운캐스팅 시도
↓
실행 중 오류 발생 가능
11. instanceof란?
instanceof는 객체가 특정 타입인지 확인할 때 사용하는 연산자입니다.
객체 instanceof 타입
결과는 true 또는 false입니다. 객체가 해당 타입이거나 그 타입의 자식 객체라면 true가 나옵니다.
Animal animal = new Dog();
System.out.println(animal instanceof Dog);
System.out.println(animal instanceof Cat);
System.out.println(animal instanceof Animal);
실행 결과는 다음과 같습니다.
true
false
true
실제 객체가 Dog이므로 Dog 타입 확인은 true입니다. 또한 Dog는 Animal의 자식이므로 Animal 타입 확인도 true입니다.
12. instanceof로 안전하게 다운캐스팅하기
다운캐스팅 전에는 instanceof로 실제 객체 타입을 확인하는 것이 안전합니다.
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
}
}
class Animal {
}
class Dog extends Animal {
void bark() {
System.out.println("멍멍 짖기");
}
}
실행 결과는 다음과 같습니다.
멍멍 짖기
animal instanceof Dog가 true일 때만 Dog로 다운캐스팅했습니다. 이렇게 하면 잘못된 형변환으로 인한 오류를 줄일 수 있습니다.
13. instanceof가 false인 경우
이번에는 실제 객체가 Cat인 경우를 보겠습니다.
Animal animal = new Cat();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else {
System.out.println("Dog 타입이 아닙니다.");
}
실행 결과는 다음과 같습니다.
Dog 타입이 아닙니다.
실제 객체가 Cat이므로 animal instanceof Dog는 false입니다. 따라서 다운캐스팅을 하지 않고 else 부분이 실행됩니다.
14. instanceof와 부모 타입
자식 객체는 부모 타입으로도 볼 수 있습니다. 따라서 자식 객체를 부모 타입으로 검사하면 true가 나옵니다.
Dog dog = new Dog();
System.out.println(dog instanceof Dog);
System.out.println(dog instanceof Animal);
실행 결과는 다음과 같습니다.
true
true
dog는 Dog 객체이면서 동시에 Animal의 한 종류입니다. 그래서 두 결과가 모두 true입니다.
15. instanceof와 null
참조변수가 null이면 instanceof 결과는 false입니다.
Animal animal = null;
System.out.println(animal instanceof Dog);
System.out.println(animal instanceof Animal);
실행 결과는 다음과 같습니다.
false
false
null은 실제 객체가 없는 상태입니다. 따라서 어떤 타입의 객체인지 확인해도 false가 나옵니다.
16. instanceof 패턴 매칭 맛보기
최근 자바에서는 instanceof를 조금 더 간단하게 사용할 수 있는 문법도 제공합니다. 이를 instanceof 패턴 매칭이라고 합니다.
기존 방식
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
패턴 매칭 방식
if (animal instanceof Dog dog) {
dog.bark();
}
조건식 안에서 Dog dog를 함께 선언하면, true인 경우 바로 dog 변수를 사용할 수 있습니다.
다만 처음 공부할 때는 기존 방식인 instanceof 확인 후 직접 다운캐스팅하는 흐름부터 익히는 것이 좋습니다.
17. 업캐스팅과 다운캐스팅 비교
업캐스팅과 다운캐스팅을 표로 비교하면 다음과 같습니다.
| 구분 | 업캐스팅 | 다운캐스팅 |
| 방향 | 자식 타입 → 부모 타입 | 부모 타입 → 자식 타입 |
| 자동 여부 | 자동 변환 가능 | 직접 명시 필요 |
| 안전성 | 안전함 | 실제 객체 타입 확인 필요 |
| 예시 | Animal a = new Dog(); |
Dog d = (Dog) a; |
| 주 사용 이유 | 다형성 활용 | 자식 고유 기능 사용 |
18. 참조변수 형변환 전체 예제
이번에는 업캐스팅, 다운캐스팅, instanceof를 함께 사용하는 전체 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog(),
new Cat(),
new Dog()
};
for (int i = 0; i < animals.length; i++) {
animals[i].sound();
if (animals[i] instanceof Dog) {
Dog dog = (Dog) animals[i];
dog.bark();
}
if (animals[i] instanceof Cat) {
Cat cat = (Cat) animals[i];
cat.meow();
}
System.out.println();
}
}
}
class Animal {
void sound() {
System.out.println("동물 소리");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("멍멍");
}
void bark() {
System.out.println("강아지 전용 기능: 짖기");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("야옹");
}
void meow() {
System.out.println("고양이 전용 기능: 야옹하기");
}
}
실행 결과는 다음과 같습니다.
멍멍
강아지 전용 기능: 짖기
야옹
고양이 전용 기능: 야옹하기
멍멍
강아지 전용 기능: 짖기
Animal[] 배열에는 Dog와 Cat 객체가 함께 들어 있습니다. 먼저 공통 메서드인 sound()를 호출하고, 이후 instanceof로 실제 타입을 확인한 뒤 자식 전용 메서드를 호출했습니다.
19. 참조변수 형변환 사용 시 자주 하는 실수
참조변수 형변환을 처음 배울 때는 아래와 같은 실수를 자주 합니다.
| 실수 | 문제점 | 해결 방법 |
| 부모 타입 변수로 자식 전용 메서드 호출 | 부모 타입에 없는 메서드는 호출할 수 없음 | 다운캐스팅 후 호출 |
| 다운캐스팅 전 실제 타입 확인 안 함 | ClassCastException 발생 가능 | instanceof로 먼저 확인 |
| 업캐스팅하면 객체가 부모 객체로 바뀐다고 생각 | 객체 자체가 바뀌는 것은 아님 | 참조 타입만 바뀐다고 이해 |
| 오버라이딩 실행 기준 혼동 | 변수 타입 기준으로 실행된다고 착각 | 실제 객체 기준으로 실행된다고 기억 |
null instanceof 타입을 true로 생각 |
null은 실제 객체가 없음 | null의 instanceof 결과는 false |
20. 직접 연습해보기
아래 코드를 직접 작성하고 실행해보세요.
public class Main {
public static void main(String[] args) {
Product[] products = {
new Product("일반 상품", 10000),
new DiscountProduct("할인 상품", 20000, 3000),
new EventProduct("이벤트 상품", 15000)
};
for (int i = 0; i < products.length; i++) {
products[i].printPrice();
if (products[i] instanceof DiscountProduct) {
DiscountProduct discountProduct = (DiscountProduct) products[i];
discountProduct.printDiscountAmount();
}
if (products[i] instanceof EventProduct) {
EventProduct eventProduct = (EventProduct) products[i];
eventProduct.printEventMessage();
}
System.out.println();
}
}
}
class Product {
String name;
int price;
Product(String name, int price) {
this.name = name;
this.price = price;
}
void printPrice() {
System.out.println(name + " 가격: " + price);
}
}
class DiscountProduct extends Product {
int discountAmount;
DiscountProduct(String name, int price, int discountAmount) {
super(name, price);
this.discountAmount = discountAmount;
}
@Override
void printPrice() {
System.out.println(name + " 할인 가격: " + (price - discountAmount));
}
void printDiscountAmount() {
System.out.println("할인 금액: " + discountAmount);
}
}
class EventProduct extends Product {
EventProduct(String name, int price) {
super(name, price);
}
@Override
void printPrice() {
System.out.println(name + " 이벤트 가격: " + (price / 2));
}
void printEventMessage() {
System.out.println("이벤트 상품입니다.");
}
}
실행 결과는 다음과 같습니다.
일반 상품 가격: 10000
할인 상품 할인 가격: 17000
할인 금액: 3000
이벤트 상품 이벤트 가격: 7500
이벤트 상품입니다.
21. 이번 글 정리
이번 글에서는 참조변수의 형변환과 instanceof에 대해 정리했습니다. 핵심 내용은 다음과 같습니다.
- 참조변수의 형변환은 상속 관계에 있는 타입 사이에서 가능하다.
- 업캐스팅은 자식 객체를 부모 타입으로 참조하는 것이다.
- 업캐스팅은 자동으로 처리된다.
- 업캐스팅 후에는 부모 타입에 정의된 멤버만 바로 호출할 수 있다.
- 오버라이딩된 메서드는 실제 객체 타입 기준으로 실행된다.
- 다운캐스팅은 부모 타입을 다시 자식 타입으로 변환하는 것이다.
- 다운캐스팅은 개발자가 직접 명시해야 한다.
- 잘못된 다운캐스팅은
ClassCastException을 발생시킬 수 있다. instanceof는 객체가 특정 타입인지 확인하는 연산자이다.- 다운캐스팅 전에는
instanceof로 실제 타입을 확인하는 것이 안전하다. null instanceof 타입의 결과는 false이다.
한 줄 요약
업캐스팅은 자식 객체를 부모 타입으로 안전하게 다루는 것이고, 다운캐스팅은 부모 타입을 다시 자식 타입으로 바꾸는 것이며, instanceof는 안전한 다운캐스팅을 위해 실제 타입을 확인하는 연산자입니다.
다음 글 예고
다음 글에서는 [Java 개념노트 34] 추상 클래스 이해하기라는 주제로 공통 기능은 제공하면서 일부 기능은 자식 클래스가 반드시 구현하도록 만드는 방법을 정리해보겠습니다.
GWDEVELBlog Java 개념노트 시리즈

'Computer Science > Java' 카테고리의 다른 글
| [Java ]개념노트 35 인터페이스 이해하기 (0) | 2026.06.13 |
|---|---|
| [Java]개념노트 34 추상 클래스 이해하기 (0) | 2026.06.13 |
| [Java] 개념노트 33 참조변수의 형변환과 instanceof 이해하기 (0) | 2026.06.13 |
| [Java] 개념노트 32 다형성이란 무엇인가? (0) | 2026.06.13 |
| [Java] 개념노트 31 super 키워드 이해하기 (0) | 2026.06.12 |