[김영한의 실전 자바 기본편] - 상속

jkky98·2024년 5월 2일
0

Java

목록 보기
7/51

상속의 필요성


상속이 필요한 이유는 프로그래밍에서 자주 보이는 문제인 "중복 문제" 때문이다.

만약 우리가 수많은 직업을 클래스로 표현해야 한다고 가정해 보자. 의사, 선생님, 엔지니어 등 1만 개의 직업 클래스를 각각 정의해야 한다면, 모든 직업 클래스에 공통적으로 포함될 이름, 전화번호, 이메일과 같은 정보를 반복적으로 작성해야 한다. 이는 마치 1만 개의 붕어빵 틀을 만들 때, 매번 같은 형태의 머리, 몸통, 꼬리를 새로 설계하는 것과 같다.

하지만 상속을 사용하면, 공통된 형태(기본적인 사람의 정보)를 가진 부모 클래스를 설계하고, 이를 바탕으로 각 직업 클래스를 정의할 수 있다. 이는 마치 공통적인 기본 틀을 만들어 놓고, 직업별로 추가적인 특징(의사: 전문 분야, 선생님: 교과목 등)을 덧붙이는 방식과 같다. 이렇게 하면 코드 중복을 줄일 수 있을 뿐 아니라, 직업 클래스를 유지보수하거나 확장할 때 훨씬 효율적이다.

결국 상속은 공통된 특성을 가진 객체를 체계적으로 관리하고 재사용성 높은 구조를 설계하기 위해 필요한 중요한 개념이다. 이를 통해 프로그래머는 반복 작업을 줄이고, 더 효율적이고 가독성 높은 코드를 작성할 수 있다.

상속 사용법

클래스명 뒤에 extends 키워드를 사용해서 부모 클래스와 연결한다.

  • 일반적 : public class Class1 () { ...
  • 상속 : public class Class1 extends Parent1 () { ...

extends 뒤에 오는 클래스는 부모클래스(슈퍼클래스)이다. 자식클래스(서브클래스)는 부모클래스를 지정하여 단방향 연결을 한다. 부모클래스는 자식클래스에 어떠한 작업도 해주지 않는다.

상속의 특징

자바는 다중상속을 지원하지 않는다. 하나의 클래스는 하나의 부모클래스만 가질 수 있다. 하지만 부모의 경우는 여러 자식 클래스를 가질 수 있다.

부모클래스가 다른 부모클래스를 가지는 것은 상관 없다.

상속시 메모리 구조

인스턴스를 생성하면 실제로 인스턴스 변수에 담기는 것은 인스턴스의 메모리 주소이다. 원래 배운 개념에서는 메모리 주소를 찾아가면 인스턴스에 대한 정보들이 담긴다고 알고있다. 하지만 상속시에는 인스턴스 주소로 메모리를 찾아가면 두 가지 인스턴스가 존재한다 첫번째 관문은 인스턴스 그 자신에 대한 정보이고 두번째 관문은 부모 인스턴스이다. 즉 상속구조를 가진 인스턴스의 경우 부모 클래스에 의해 부모 인스턴스 정보도 메모리에 생성된다.

만약 부모의 메서드를 호출했다고 한다면 우선 자바는 자식의 인스턴스에서 메서드를 먼저 찾고 없으면 부모에서 찾는다. 부모에서도 없으면 컴파일 에러가 발생한다.

오버라이딩

오버라이딩은 부모메서드를 자식메서드에서 재정의 하는 것이다. 부모메서드에 정의된 코드는 보통적인 기능을 가질 것이지만 만약 특정한 자식클래스의 메서드에서 수정이 필요할 경우 오버라이딩을 이용하여 비슷하지만 부모의 메서드와는 다른 내부로직을 가지는 동일한 이름의 메서드를 작성할 수 있다.

오버라이딩시에는 특별하게 @Override라는 Annotation을 작성한다. 이것은 주석이지만 자바가 읽고 특별한 기능을 제공한다.

실제로 이 인스턴스의 부모 클래스에는 동일한 move()가 정의되어있다. 하지만 @Override를 통해 이 메서드가 오버라이드된 메서드임을 나타낸다.

Override Annotation은 왜 필요한가?
실제로 위 캡쳐에서 @Override를 적지 않아도 move()는 자식의 move()로 실행된다. 그 이유는 자식 인스턴스에서 move()를 먼저 찾기 때문이다. 하지만 @Override를 쓰지 않았을 경우, 자바는 이 메서드가 오버라이드의 목적이라는 것을 모르기 때문에 혹여나 실수로 인한 move()를 moveee()로 적어도 설계과정에서 에러경고를 띄우지 않는다. 그리고나서 move()를 호출한다면, 자식에 move()가 없기에 부모의 move()를 가져오게 된다. 흔한 실수는 아니겠지만 프로그래밍시 의도하지 않은 문제를 발생시킬 확률을 원천 차단할 수 있으므로 오버라이딩의 의도로 코딩한다면 해당 annotation은 그냥 습관처럼 추가하는 것이 좋을 것 같다.

오버라이딩시에 부모의 메서드보다 자식의 오버라이드 메서드의 접근제어자가 더 제약이 많아질 수는 없다. 예로 protected한 부모 메서드가 자식 오버라이드에서는 private으로 바뀔 수 없다.

또한 생각해보면 당연한 것들 즉 static, final, private에서 오버라이딩은 불가능하며, 생성자또한 오버라이딩할 수 없다.

당연히 매개변수 타입을 맞춰야하며 개수도 맞아야 한다.

super

super은 super class의 super을 나타내며, 말 그대로 super인스턴스를 나타낸다.

자신의 인스턴스를 가리키는 것이 this.이었다. this()가 생성자를 호출하는 것이듯 super()또한 부모의 생성자를 호출하는 것이다.

부모 클래스와 extends된 자식 클래스로 만들어진 인스턴스를 생성하면 메모리 에는 (자식인스턴스 + 부모인스턴스)의 메모리 주소가 변수에 담긴다. 부모인스턴스도 생성되어야 자식에서 사용가능 하기에 메모리에는 두 정보가 모두 생성된다. 우리가 this나 super로 호출하지 않는다면 자바는 메서드나 필드를 찾기위해 메모리에서 자식인스턴스를 먼저 방문하고 그 후 부모를 방문한다.

만약 super.메서드()라 한다면 자식을 지나쳐 바로 부모로 갈 것이다. this.메서드()라 한다면 부모의 검색은 이루어지지 않을 것이다.

부모 클래스의 생성자가 기본생성자(no parameter)라면 자식 생성자는 super() 부모생성자 호출을 자동적으로 호출한다.

하지만 기본생성자가 아닐 경우에는 직접 super(파라미터);를 자식 생성자에 작성해주어야 한다.

profile
자바집사의 거북이 수련법

0개의 댓글