Goal : 상속이 무엇인지 알아보고, 코드에서는 어떻게 구현되는지 알아본다.
상속이란?
객체 지향 프로그래밍(OOP)에서, 상속(inheritance)은 객체들 간의 관계를 구축하는 방법이다. 클래스로 객체가 정의되는 고전 상속에서, 클래스는 기반 클래스, 수퍼클래스, 또는 부모 클래스 등의 기존의 클래스로부터 속성과 동작을 상속받을 수 있다. 그 결과로 생기는 클래스를 파생 클래스, 서브클래스, 또는 자식 클래스라고 한다. 상속을 통한 클래스들의 관계는 계층을 형성한다. 프로토타입 기반 프로그래밍에서는, 객체가 클래스를 따로 정의할 필요 없이 다른 객체로부터 직접 정의될 수 있다. 이러한 특징을 차등 상속이라고 부른다.
백문이 불여일타. 코드로 개념을 이해해보자!
예제 1. 필수 값이 없는 부모 클래스 - 기본 생성자
Person Class
package com.in28mins.oop.inheritance;
public class Person {
private String name;
private String email;
private String phoneNumber;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
}
Person class는 이름, 이메일, 전화번호 (개인정보)를 가지고 있는 클래스이다.
여기서 상속을 사용하지 않을 때의 StudentWithoutHeritance Class를 살펴보자.
StudentWithoutHeritance Class - 상속을 사용하지 않을 때
package com.in28mins.oop.inheritance;
public class StudentWithoutHeritance {
private String name;
private String email;
private String phoneNumber;
private String college;
private int year;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getCollege() {
return college;
}
public void setCollege(String college) {
this.college = college;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
}
StudentWithoutHeritance 클래스에서는 상속을 사용하지 않기 때문에 필요한 각각의 멤버 변수 및 getter, setter를 선언 해줘야 하고, 그로 인해 같은 코드(Person 클래스 속성정보와 동일)가 여러 곳에 존재하게 된다. 또한 해당 정보에 수정이 생긴다면, Student와 Person클래스 모두 코드 변경을 해줘야 하므로 번거롭다.
- 코드 중복
- 여러 곳에 존재하는 동일한 코드로 인한 번거로운 유지보수
그런데 상속을 사용하면 위의 문제점들을 해결할 수 있다. 아래 코드로 살펴보자.
Student Class - 상속 사용 시
package com.in28mins.oop.inheritance;
public class Student extends Person{
private String collegeName;
private int year;
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "Student{" +
"name='" + getName() + '\'' +
"email='" + getEmail() + '\'' +
"collegeName='" + collegeName + '\'' +
", year=" + year +
'}';
}
}
상속을 사용할 때는 Student extends Person과 같이 해당 클래스 extends 상속 받을 클래스 로 표현하면 된다.
이렇게 표현하면 Person 클래스에 존재하는 모든 정보(멤버변수, 생성자, 메소드)등을 가질 수 있다.
이는 is a 개념이 성립되기 되므로, Student is a Person의 관점으로 보면 Person의 속성이 Student에도 존재하기 때문이다. 그러므로 Student에는 Person과 동일한 코드가 존재하지 않아도 된다. 이는 중복 코드를 줄일 수 있고, 수정시에도 상속받는 부모 클래스의 정보만 수정해주면 된다.
- is a 관계
- 코드 재사용
- 변경 시에는 부모 클래스만 수정하면 됨 유지보수가 용이
StudentRunner Class - 1
package com.in28mins.oop.inheritance;
public class StudentRunner {
public static void main(String[] args){
Student student = new Student();
student.setCollegeName("dev univ");
student.setYear(2);
student.setName("rosie");
student.setEmail("rosiekim128@gmail.com");
System.out.println(student);
}
}
- 부모 클래스의 setName(), setEmail()의 메소드를 바로 사용할 수 있음
결과
Student{name='rosie'email='rosiekim128@gmail.com'collegeName='dev univ', year=2}
StudentRunner Class - 2
package com.in28mins.oop.inheritance;
public class StudentRunner {
public static void main(String[] args){
Person person = new Person();
String value = person.toString();
System.out.println(person);
System.out.println(value);
}
}
- 명시적으로 코드에는 표기하지 않지만 묵시적으로 Person extends Object - person 클래스가 Object를 상속 받아 Object의 toString() 메소드를 사용할 수 있다. ( hashCode(), equals(), clone() 등 이하 동일 )
- Person 클래스 뿐만 아니라 모든 클래스는 Object 클래스를 상속 받는다
결과
com.in28minutes.oop.inheritance.Person@2a139a55
com.in28minutes.oop.inheritance.Person@2a139a55
예제 2. 특정 멤버 변수를 필수 값으로 가지는 부모 클래스
Person Class
package com.in28minutes.oop.inheritance;
import java.util.HashMap;
public class Person {
private String name;
private String email;
private String phoneNumber;
Person(String name){
this.name = name;
System.out.println("person constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return name+" "+email+" "+phoneNumber;
}
}
- 새로운 person 인스턴스 생성시, name을 필수 파라미터로 받는다.
Employee Class
package com.in28minutes.oop.inheritance;
import java.math.BigDecimal;
public class Employee extends Person{
private String title;
private String employer;
private char employeeGrade;
private BigDecimal salary;
Employee(String name, String title){
super(name);
this.title = title;
System.out.println("employee constructor");
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getEmployer() {
return employer;
}
public void setEmployer(String employer) {
this.employer = employer;
}
public char getEmployeeGrade() {
return employeeGrade;
}
public void setEmployeeGrade(char employeeGrade) {
this.employeeGrade = employeeGrade;
}
public BigDecimal getSalary() {
return salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
@Override
public String toString() {
return "title=" + title + ", employer=" + employer + ", employeeGrade=" + employeeGrade + ", salary=$"
+ salary + ", name=" + getName() + ", email=" + getEmail() + ", phoneNumber="
+ getPhoneNumber();
}
}
- Empoyee 클래스는 Person 클래스를 상속 받았기 때문에, 모든 변수와 파라미터를 사용할 수 있다.
- Person 클래스는 name을 필수 파라미터로 가지므로, 상속 받은 Employee 클래스에서도 name은 필수 파라미터다.
- Employee 클래스는 title을 필수 파라미터로 가진다.
위의 요구조건에 부합하는 생성자를 살펴보자.
Employee(String name, String title){
super(name);
this.title = title;
}
String name은 Person 클래스의 필수 파라미터, String title은 Employee클래스의 필수 파라미터 두개를 매개변수로 가지고 super()메소드를 통해 부모 클래스의 메소드를 호출하면 Person클래스의 Person(String name){ this.name = name; System.out.println("person constructor"); }이 실행된다. this.title= title은 기본 생성자 생성 규칙을 따른다.
결과
person constructor
student constructor
예제 3. 다중 상속 불가
Teacher Class
package com.in28minutes.oop.inheritance;
public class Teacher{
private String className;
private int studentCnt;
public int getStudentCnt() {
return studentCnt;
}
public void setStudentCnt(int studentCnt) {
this.studentCnt = studentCnt;
}
}
Teacher 클래스가 Employee클래스와 Person클래스 모두 상속 받고 싶을 때 Teacher extends Employee, Person 라고 하면 되지 않을까? 싶지만 자바는 다중상속을 지원하지 않는다. (별개로 c++에서는 다중 상속이 된다고 한다.)
예제 4. instanceof
TeacherRunner Class
package com.in28minutes.oop.inheritance;
public class TeacherRunner{
public static void main(String[] args) {
Person person = new Person("person");
Teacher teacher1 = new Teacher("rose", "teacher", "java");
Teacher teacher2 = new Teacher("kim", "teacher", "english");
Student student1 = new Student("yeye", "3nius Univ");
Student student2 = new Student("yuja", "3nius Univ");
System.out.println(teacher1 instanceof Teacher);
System.out.println(teacher2 instanceof Teacher);
System.out.println(teacher1 instanceof Employee);
System.out.println(teacher1 instanceof Person);
System.out.println(teacher1 instanceof Object);
System.out.println(person instanceof Teacher);
System.out.println(student1 instanceof Student);
System.out.println(student1 instanceof Person);
System.out.println(student2 instanceof Object);
//System.out.println(student2 instanceof Teacher);
}
}
instanceof 연산자는 상속관계에서 부모객체인지 자식객체인지 확인하는데 사용하고, 객체가 특정 클래스나 인터페이스로부터 생성됬는지 판별하여 true or false로 반환한다.
true일 때는 해당타입으로 형변환이 가능하다.
결과
true
true
true
true
true
false
true
true
true
Person은 Teacher의 부모 클래스 이므로 false 반환
----------------------------------------------
System.out.println(student2 instanceof Teacher); 주석 제외 후 실행 시 결과
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Incompatible conditional operand types Student and Teacher
at com.in28minutes.oop.inheritance.TeacherRunner.main(TeacherRunner.java:22)
정리
상속이란 자식 클래스가 부모 클래스를 extends(확장)하여 부모 클래스의 멤버변수와 메소드를 바로 사용할 수 있는 개념을 뜻한다.
특징
- is a 관계 (Student is a Person)
- 자식클래스 extends 부모클래스
- 부모클래스의 모든 변수와 속성을 바로 사용할 수 있다
- 모든 클래스는 Object를 상속받는다
- 다중 상속 불가
- 코드 재사용성
Person | Student |
부모 클래스 | 자식 클래스 |
Super 클래스 (슈퍼 클래스) | Sub Class (서브 클래스) |
기반 클래스 | 파생 클래스 |
** 주의
상속은 구성의 개념과 비슷해보이지만 다르다! 상속은 is a 관계인 반면, 구성은 has a 관계임!
구성 포스팅 참고!
https://dev-rosiepoise.tistory.com/24?category=1295685
객체지향의 꽃 상속 ... ★ 이전에 구성을 정리해둔 덕에, 둘의 차이점과 개념을 확실히 잡고 막연하게 상속을 남발하지 않을 수 있을 것 같다. 재밌다 재밌어!!!!
@ The teacher Mr. Ranga 강의를 참조하고 있습니다.
'JAVA > 객체지향의 원리' 카테고리의 다른 글
[OOP] interface / 인터페이스 (0) | 2022.09.07 |
---|---|
[OOP] abstract / 추상화 (0) | 2022.09.05 |
[OOP] Composition / 구성 (0) | 2022.08.15 |
[OOP] Constructor / 생성자 (0) | 2022.08.09 |
[OOP] Encapsulation / 캡슐화 (0) | 2022.07.31 |