Java
Java 상속(Inheritance) 이해하기 - 업캐스팅, 다운캐스팅
오은이
2025. 4. 14. 13:38
우선, 메서드와 생성자의 정확한 차이부터 짚고 가야한다.
메서드와 생성자 차이
구분 | 메서드 (Method) | 생성자 (Constructor) |
목적 | 객체가 가진 동작/기능을 정의 | 객체가 생성될 때 초기화 |
이름 | 아무 이름 가능 | 클래스 이름과 동일해야 함 |
반환 타입 | 있어야 함 (void, int, etc) | ❌ 없음 (void도 안 씀) |
호출 시점 | 객체가 만들어진 후 호출 | new 클래스명() 할 때 자동 호출 |
호출 방법 | 객체.메서드() | new 클래스() |
상속/오버라이딩 | 메서드는 오버라이드 가능 | 생성자는 상속/오버라이드 불가 (다만 super()로 호출 가능) |
업캐스팅과 다운캐스팅
개념 | 방향 | 설명 |
업캐스팅 | 자식 → 부모 | 자식 객체를 부모 타입으로 참조 (자동) |
다운캐스팅 | 부모 → 자식 | 부모 타입 참조를 자식 타입으로 형변환 (명시적) |
단, 실제 객체가 자식 타입일 때만 가능 |
Parent p = new Child(); // 업캐스팅 (자동)
Child c = (Child)p; // 다운캐스팅 (명시적으로)
예제1) 정보처리기사 실기 2023년 3회
public class main{
public static void main(String[] args) {
A b = new B();
b.paint();
b.draw();
}
}
class A {
public void paint() {
System.out.print("A");
draw();
}
public void draw() {
System.out.print("B");
draw();
}
}
class B extends A {
public void paint() {
super.draw();
System.out.print("C");
this.draw();
}
public void draw() {
System.out.print("D");
}
}
출력 결과
BDCDD
코드 | 의미 |
this.draw() | 오버라이딩된 메서드 호출됨 (B) |
draw() | 오버라이딩된 메서드 호출됨 (B) |
super.draw() (B 내부) | 부모의 draw 호출 가능 (A) |
A 내부에서 강제로 A의 draw만 호출 | 불가능 → 다른 메서드로 분리해서 써야 함 |
예제2) 정보처리기사 실기 2024년 1회
class classOne {
int a, b;
public classOne(int a, int b) {
this.a = a;
this.b = b;
}
public void print() {
System.out.println(a + b);
}
}
class classTwo extends classOne {
int po = 3;
public classTwo(int i) {
super(i, i+1);
}
public void print() {
System.out.println(po*po);
}
}
public class main {
public static void main(String[] args) {
classOne one = new classTwo(10);
one.print();
}
}
출력 결과
9
코드 | 의미 |
super(i, i+1) | 부모 클래스의 생성자 호출 (초기화) |
super.print() | 부모 클래스의 print() 메서드 호출 |
one.print() | (오버라이딩된) 자식 클래스의 print() 호출 |
예제3) 정보처리기사 실기 2023년 1회
class Parent {
int x = 100;
Parent() {
this(500);
}
Parent(int x) {
this.x = x;
}
int getX() {
return x;
}
}
class Child extends Parent {
int x = 4000;
Child() {
this(5000);
}
Child(int x) {
this.x = x;
}
}
public class Main {
public static void main(String[] args) {
Child obj = new Child();
System.out.println(obj.getX());
}
}
출력 결과
500
① new Child()로 child의 기본 생성자 호출
② 기본 생성자에서 this(5000) 호출하여 Child(int x)로 넘어감
③ Child(int x)는 super() 클래스가 숨겨진 형태
Child(int x) {
super(); // 자동 삽입됨 ⬅ 부모 기본 생성자 호출
this.x = x; // Child.x = 5000
}
④ 그러므로 super(); → Parent() 생성자 호출
⑤ Parent(int x) 호출, Parent.x = 500
⑥ this.x → Parent의 x
⑦ 500 리턴
‼️생성자 호출용 this()가 없다면 super()가 자동 삽입됨
예제4) 정보처리기사 실기 2025년 1회
class Parent {
static int total = 0;
int v = 1;
public Parent() {
total += (++v);
show();
}
public void show() {
total += total;
}
}
class Child extends Parent {
int v = 10;
public Child() {
v += 2;
total += v++;
show();
}
@Override
public void show() {
total += total * 2;
}
}
public class Gisafirst {
public static void main(String[] args) {
new Child();
System.out.println(Parent.total);
}
}
출력결과
54
① new Child(); 실행됨
② Child() 생성자 진입
③ 자동으로 super(); 호출
④ super(); → Parent() 생성자 실행 (v = 2, total = 2)
⑤ Parent() 내에서 show() 호출
⑥ 오버라이딩된 Child.show() 실행 (total = 6)
⑦ 그 후에 다시 Child 생성자 나머지 코드 실행 (v = 12, total = 18, v = 13)
⑧ show() 호출 → Child의 show 호출 (total = 18 + 18 * 2 = 54)
⑨ Parent.total 호출 → 54 출력