[JAVA] 상속

쥬니·2022년 9월 20일
1

공부

목록 보기
2/11

❗ 개인적으로 공부했던 내용을 복습하고 정리하기 위한 글입니다! 따라서 내용이 정확하지 않을 수 있습니다!

객체지향의 3요소

흔히 말하는 객체지향의 3요소, JAVA의 3요소라고 해야 하나.... 학교다닐 때부터 귀에 딱지가 앉을만큼 들었고, 학원에서도 또 듣는다! 그만큼 아주 중요하다는 뜻이다. 객체지향의 3요소는 캡슐화(Encapsulation), 다형성(Polymorphism), 상속(Inheritance) 이 있다. 오늘 이 포스트에서는 그 중에서도 상속에 대해 다뤄보고자 한다.

상속(Inheritance)

먼저 상속을 사전에 검색해 보면 다음과 같이 나온다.

  1. 뒤를 이음.
  2. 법률 일정한 친족 관계가 있는 사람 사이에서, 한 사람이 사망한 후에 다른 사람에게 재산에 관한 권리와 의무의 일체를 이어 주거나, 다른 사람이 사망한 사람으로부터 그 권리와 의무의 일체를 이어받는 일.

부모의 것을 이어받거나, 뒤를 잇는 것이라고 생각하면 편하다. 자바에서도 사전적 의미와 크게 다르지 않다. 부모 클래스를 상속받아 자식 클래스를 생성하면, 부모의 클래스를 확장하여 생성할 수 있다. +α가 되는 샘이다.

상속은 왜 쓰는데?

그럼 이 기능은 왜 쓰는 것일까? 위에 말했듯 부모 클래스를 확장하여 자식 클래스를 생성할 수 있다. 중복되는 부분을 제거할 수 있고, 클래스의 재사용을 유용하게 한다. 또한 코드를 공통적으로 관리할 수 있어서 유지보수에 기여한다.

사용법

위에서 강조했던 확장이라는 단어를 기억하면 좋을 것 같다. 상속은 부모 클래스를 확장하여 자식 클래스를 정의하는 것이기 때문에, extends키워드를 사용한다. 아래 예시에 먼저 부모 클래스인 Parent.java를 정의하였다.

// Parent.java
public class Parent {

	private int x;
	private int y;

	public Parent() {}

	public Parent(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	@Override
	public String toString() {
		return "Parent [x=" + x + ", y=" + y + "]";
	}

}

다음은 자식 클래스인 Child.java를 정의하였다.

//Child.java
public class Child extends Parent {

	private int z;

	public Child() {}

	public Child(int x, int y, int z) {
    	super(x, y);
		this.z = z;
	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}

	@Override
	public String toString() {
		return "Child [z=" + z + "]";
	}

}

즉, 상속을 정의할 때에는 다음과 같이 작성한다.

public class 자식클래스명 extends 부모클래스명

클래스 형변환

흔히 객체는 형변환이 안 된다고 한다. 그러나 상속 구조의 경우에는 클래스간의 형변환이 가능하다. 클래스 형변환에는 두가지가 있는데, 바로 업 캐스팅(Up Casting)과 다운 캐스팅(Down Casting)이 있다. 또한 이런 클래스 형변환은 다형성을 구현하는 것이라고 생각할 수 있다.

Up Casting

상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입의 객체 주소를 받을 수 있음

즉, 자식 타입 객체를 부모 타입 객체로 사용하려고 하면 별다른 연산자 없이 가능하다.

Child c = new Child(1, 2, 3);
Parent p = c;		// 업캐스팅
		
System.out.println(p.getX());
System.out.println(p.getY());
//System.out.println(p.getZ());	// undefined 오류 발생!!

위의 코드는 Child클래스의 객체를 생성할 때, 매개변수로 1, 2, 3과 같이 주었다. 그러면 Child.java에 선언 된 정수형 숫자 3개를 매개변수로 받는 생성자가 호출되어 c라는 객체가 생성되었다. 그 다음, Parent p라는 객체를 선언할 때, 이 객체에 바로 윗줄에서 생성한 객체 c를 대입하였다. (정확히는 같은 주소를 참조하도록 함!) 그러면 업캐스팅이 일어났고, Parent.java에 선언된 getter 메소드를 사용할 수 있게 되는 것이다. 그러나 Parent.java에는 getZ() 메소드가 선언되어 있지 않아 undefined 오류가 발생하였다.

Down Casting

위의 업 캐스팅과 정반대가 되는 것이 바로 다운 캐스팅이다. 다운 캐스팅의 의미는 다음과 같다.

자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형 변환하는 것

이 때 주의할 점은 다운 캐스팅을 할 때에는 반드시 Casting 연산자가 필요하다는 것이다.

Parent p = new Child(1, 2, 3);		// 다운 캐스팅
// System.out.println((Child)p.getZ());
System.out.println(((Child)p).getZ());

이 때 주의할 점은, ((Child)p).getZ()와 같이 사용하였다는 점인데, 연산자 우선순위에 의해 캐스팅 연산자가 참조 연산자보다 우선순위가 낮아 오류 발생하기 때문이다.

instanceof 연산자

instanceof 연산자는 현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용한다. 클래스 타입이 맞으면 true, 아니라면 false를 반환한다.

Parent p = new Child(1, 2, 3);
System.out.println(p instanceof Child);
System.out.println(p instanceof Parent);

위 두 출력값은 모두 true가 나오는데, 그 이유는 다운 캐스팅이 일어나서 Child, Parent 클래스를 모두 참조하기 때문이다.

마치며...


자바에서 너무나도 중요한 개념이고, 절차지향 언어에서 찾아볼 수 없었던 개념이기도 하다. 그래서 예전에 대학교 전공시간에서 배울 때에도 잘 이해가 안 되었었는데, 여전히 어려운 개념이어서 정리하게 되었다.
개인적으로 객체지향의 3요소는 각각 딱 잘라서 이건 상속! 이건 다형성! 이건 캡슐화! 이렇게 나눌 수 없다고 생각한다. 만약 상속을 한 부모 클래스의 필드의 접근지정자가 private가 아닌 protected로 선언되었다고 하면, 자식 클래스에서 getter / setter가 아닌 필드 그 자체로 접근할 수 있다. 그런데 이건 캡슐화 중 하나라고도 한다. 또한 위에서 다룬 클래스의 형변환도 다형성의 예시 중 하나라고 할 수 있다. 따라서 "객체지향"을 공부할 때에는 3요소를 딱 나누어 공부하는 것이 아닌, 각각의 연관관계를 생각하며 공부해야 한다고 생각한다.

끝!😎

0개의 댓글