상속 - 클래스 확장
- 기존의 클래스들을 효율적으로 재사용하여 프로그래밍을 하는것이 중요하다.
- 여러 클래스들을 재사용하는 방안으로는
- 상속
- 결합
- 슈퍼클래스(상속을 주는 부모클래스)와 서브클래스(상속을 받는 자식클래스)
- Employee 클래스의 몇 가지 기능을 보유하면서 관리자가 어떻게 다른지 명시
<예시 1> extends를 통해 슈퍼클래스를 상속받는다.
public class Manager extends Employee {
추가된 필드
추가된 메소드 또는 오버라이딩된 메소드
}
=> Employee 클래스에 있는 모든 필드와 메소드를 Manager 클래스에 가져온다.
서브 클래스 메소드 정의와 상속
<예시 2> Manager 클래스에 상여금 인스턴스 변수와 메소드 추가
public class Manager extends Employee {
private long bonus; // 추가 멤버변수
…
public void setBonus(long bonus) { // 추가 메소드
this.bonus = bonus;
}
}
메소드 오버라이딩
<예시 3> 오버라이딩 : 서브 클래스에서 슈퍼클래스 메소드를 수정해야 할 때
public class Manager extends Employee {
…
@Override // 이 어노테이션은 생략 가능하지만 이 메소드는 오버라이딩 됬다는 알려주는 역할을 한다.
// 그리고 이 어노테이션이 있으면 이 메소드는 정상적으로 오버라이딩 됬다는걸 의미함.
public long getSalary() { // 슈퍼클래스의 메소드를 오버라이드
return super.getSalary() + bonus; // 부모클래스에 있는 메소드의 이름과 똑같이 쓰고, 메소드 내용을 재정의해주면 된다.
}
}
상속 - is-a relationship
is-a 관계 (=상속관계)
<예시 1> 서브 클래스 객체를 슈퍼클래스 타입 변수에 할당 가능
// < Manager is an Employee >
Employee emp = new Manager(); //가능
=> 왼쪽은 employee, 오른쪽은 manager 타입(클래스)지만 정상적으로 할당이 된다.
타입이 다르지만 할당이 정상적으로 되려면 is-a 관계가 성립되어야 한다.
오른쪽 객체가 왼쪽 타입의 자식일때 is-a 관계 성립.
=> Manager is an Employee
- 모든 직원은 매니저다 -> 성립 안됨
- 매니저는 직원이다 -> 성립 됨
- 왼쪽이 더 제너럴한 타입이, 오른쪽에 더 specific한 타입이 온다.
동적 메소드 조회(Runtime Binding)
- 메소드가 호출될 때 가상머신은 객체의 실제 클래스를 살펴보고 호출
- 자바에서는 모든 메소드가 오버라이딩 가능한 대상.
<예시 1>
Employ emp = new Manager();
emp.getSalary(); // 실체 객체인 Manager 클래스의 getSalary 메소드가 호출됨
=> 이때의 getSalary는 emp의 타입은 Employee지만 실제 객체는 Manager임. 그래서 getSalary는 Manager 클래스의 메소드가 호출됨. 자바에서는 모든 메소드는 동적메소드가 호출된다.
final 클래스와 final 메소드
- 메소드를 final로 선언하면 서브 클래스에서 오버라이딩 불가
- 일명 최종 메소드
- 클래스를 final로 선언하면 서브 클래스를 만들 수 없음
- 일명 최종 클래스
- Java API 사례 : String, LocalTime, URL… (더 확장할 필요가 없이 이 클래스 그대로 사용한다)
<예시 1>
public final class Executive extends Employee {
…
public final String getName() { // 이 메소드를 확장하지 않고 런타임바인딩 하지 않는다. -> 속도가 좀 더 빨라짐.
return this.name;
}
}
추상 클래스와 추상 메소드
- 클래스는 구현없는 메소드를 정의해서 서브 클래스에게 메소드 정의를 강제함
- 구현이 없는 메소드를 추상 메소드라고 함
- 추상 메소드가 포함된 클래스를 추상 클래스라고 함
- 추상 메소드와 추상 클래스에는 abstract 제어자를 붙임
<예시 1>
public abstract class UnregularEmployee extends Employee {
…
public abstract String getSalary();
}
추상클래스는 바로 쓸 수 없고, 누군가가 상속받아서 정상적으로 만들어줘야 함. 추상 클래스를 상속받게되면 추상메소드도 같이 상속받게 되는데, 오버라이딩을 해서 재정의해서 concrete class로 만들어줘야 사용 가능하다. 추상클래스는 미완성 클래스.
Object 클래스
- 자바에서 모든 클래스는 Object 클래스를 상속받는다.
- 클래스가 명시적으로 상속받는 클래스가 없으면 Object 클래스를 확장(상속)
- 자바에서 모든 클래스는 서로 상속받고 상속을 주는 관계가 있는데 계층트리 모양이다.
- 이 클래스들의 가장 위에 있는것이 Object 클래스다.
<예시 1>
public class Employee {…}
//위 코드는 다음과 같다.
public class Employee extends Object {…}
그래서 모든 자바 클래스는 object 클래스가 갖고있는 속성을 모두 가지고 있다.
ThinkPoint
컴포지션이란?
클래스를 상속하는 대신 기존 클래스(super class로 하려 했던)의 인스턴스를 참조하는 private 필드를 서브 클래스로 만들고자 했던 클래스에 만든다.
이런 패턴을 컴포지션이라고 한다.
그리고 새 클래스의 각 인스턴스 메소드에서는 기존 클래스에 포함된 함수들을 호출하여 결과를 반환해준다. 이것을 포워딩 (forwarding)이라고 한다.
이렇게 함으로서 새 클래스는 기존 클래스의 내부 구현에 종속되지 않으며, 새로운 메소드를 추가하더라도 새 클래스에는 영향을 주지 않는다.
이런 클래스를 wrapper class라고 한다.
실습 코드
1. 부모클래스 생성
inheritance 패키지 하에 Employee 클래스를 만들고 Payable 인터페이스를 받는다. 인터페이스는 다음 강의에서 설명할 예정이다.
자바에서 사용자 정의 클래스 포함 모든 클래스는 Object라는 상위 클래스에서 비롯되기 때문에 사실 extends Object가 생략된 것이다.
자바는 통상적으로 클래스 내의 멤버변수는 private으로 설정하고 하위 클래스에서 상속받아 객체를 생성해서 사용한다.
하지만 이런 저런 다양한 방법들을 사용해보기위해 하위 클래스에서만 가져다 쓸 수 있는 protected를 접근제한자로 사용해봤다.
생성자는 하위 클래스에서 파라미터로 넘겨받은 name과 salary를 Employee 클래스 내에서 가공하기 위해 생성한다.
추상메소드 paySalary를 만듦으로서 이제 Employee 클래스의 하위클래스들은 paySalary 메소드를 각 클래스 내에서 재정의해서 구현해줘야 한다.
게터를 만들어서 다른 클래스에서 게터 메소드로 멤버변수를 호출할 수 있게 한다.
toString 메소드로 모든 저장된 멤버변수를 출력한다.
package com.acompany.inheritance;
// 추상클래스화시키기
public abstract class Employee implements Payable/* extends Object가 생략된것 */ {
// 멤버변수
protected String name;
protected long salary;
// 생성자
public Employee(String name, long salary) {
this.name=name;
this.salary=salary;
}
// 추상메소드 만들기
public abstract long paySalary();
//=> Employee를 상속받는 모든 클래스들은 paySalary 메소드를 구현해줘야 함.
// 게터
public String getName() {
return name;
}
public long getSalary() {
return salary;
}
// toString 메소드 추가
@Override // 오브젝트 클래스의 toString 메소드의 원본을 하위클래스인 Employee에서 오버라이딩함.
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
2. 자식 클래스 생성
Employee를 상속받는 하위클래스 Manager을 만든다.
Manager is an Employee, 즉 is-a 관계가 성립한다.
Manager 클래스는 Employee 클래스 내의 모든 멤버변수와 메소드를 상속받고, bonus라는 Manager 클래스에서만 사용할 멤버변수를 추가로 만든다.
Generator에서 자동으로 생성자를 추가하게 되면 부모클래스의 멤버변수인 name과 salary, 자식클래스의 멤버변수인 bonus의 생성자가 자동으로 생긴다. 부모클래스의 멤버변수는 부모클래스의 생성자를 호출하므로 super가 붙는다.
부모 클래스의 추상메소드였던 paySalary를 구현해준다. 연봉을 12개월로 나누고 그 달의 상여금을 더한 값을 반환해준다.
이 때, salary 멤버변수를 부모클래스에서 private으로 설정하였다면 getSalary라는 게터를 통해서만 호출 가능하다. 만약 protected로 접근제한자를 설정했다면 게터를 사용할 필요 없이 바로 this.salary를 해도 무방하다.
자식 클래스의 멤버변수인 bonus에 대한 게터를 생성한다.
package com.acompany.inheritance;
public class Manager extends Employee { // Employee클래스의 추상메소드를 구현하지 않았기 때문에 에러가 남
// 매니저 클래스에서 사용할 멤버변수 생성
private long bonus;
// 생성자 추가. 부모 클래스에서 받은 name, salary와 자식클래스에서 새로 만든 멤버변수 bonus까지 생김.
public Manager(String name, long salary, long bonus) {
super(name, salary); // 부모클래스의 생성자를 호출
this.bonus = bonus; // 자기 자신의 생성자
}
// 부모클래스의 추상메소드 구현하기
@Override
public long paySalary() {
return this.getSalary()/12+this.bonus; // 멤버변수 salary가 private이기 때문에 직접 this.salary는 못씀.
// 만약 멤버변수가 private이 아니라 protected면 자식 클래스에게는 보여주겠다는 것이니 가능.
}
public long getBonus() { //게터는 name과 salary는 부모클래스에서 상속받았기 때문에 자기 자신의 멤버변수인 bonus만 생성됨
return bonus;
}
@Override
public String toString() {
return super.toString()+ // 부모클래스의 name과 salary도 출력하고싶다면 super이라는 부모클래스를 지칭하는 객체에 toString() 메소드를 포함해줌.
"Manager{" +
"bonus=" + bonus +
'}';
}
}
3. final 자식 클래스 생성
앞서 배운 final 메소드, final 클래스를 활용해보기 위해 매니저를 제외한 다른 사원들에 대한 급여를 관리하는 RegularEmployee 클래스를 만든다. final 클래스로 선언함으로서 RegularEmployee 클래스의 하위클래스는 선언할 수 없고, 이 클래스 내의 메소드들은 오버라이딩을 할 수 없게된다.
메소드에 final이 붙으면 상수가 된다고 하는데, 자바의 변수 PI처럼 일정한 고정된 값을 지니게 된다. 즉, 더이상 바뀌지 않는다.
Manager에서 했던것처럼 똑같이 생성자를 만든다. name과 salary는 부모클래스의 생성자를 사용하니 super이 붙는다.
Employee 부모클래스의 추상메소드 paySalary를 오버라이딩해 구현해준다. final 메소드의 장점은 속도가 빠르다는 것이다.
package com.acompany.inheritance;
public final class RegularEmployee extends Employee {
// 이 클래스를 final로 선언하게 되면 하위 클래스를 만들 수 없게된다.
// 즉, 이 클래스의 메소드들은 오버라이딩 대상이 아니게 됨.
private final int transportationAllowance;
// 멤버변수에 final이 붙으면 상수가 됨. 더이상 바뀌지 않는다.
public RegularEmployee(String name, long salary, int transportationAllowance) {
super(name, salary);
this.transportationAllowance = transportationAllowance;
}
@Override
public final long paySalary() {
// 메소드에 final이 붙어버리면 이 메소드는 오버라이딩 되지 않는다.
// final 메소드는 cpu를 많이 차지하는 복잡한 연산이면 final이 붙으면서 전기 바인딩이 일어나며 수행 속도가 훨씬 빨라진다.
return this.salary/12+this.transportationAllowance;
}
public int getTransportationAllowance() {
return transportationAllowance;
}
@Override
public String toString() {
return "RegularEmployee{" +
"transportationAllowance=" + transportationAllowance +
'}';
}
}
4. 구현 클래스 생성
마지막으로 이 프로그램을 직접 실행해볼 메인 함수를 만든다.
Employee에 대한 emp라는 객체를 세우고 출력한 것은 Employee 클래스를 추상클래스로 만들기 전이다.
추상클래스로 만든 후에는 객체를 세울 수 없다. 미완성 클래스이기 때문이다.
하지만 이 추상클래스의 추상메소드 getSalary를 Manager 하위클래스에서 구현했기 때문에 Manager 객체를 세우는 것은 가능하다. name, salary, bonus 세 항목을 파라미터로 입력한다.
toString 메소드는 Object클래스에 속한 메소드이고, 원칙적으로는 클래스의 주소값을 반환한다.
하지만 우리가 Manager에서 toString을 오버라이딩하여 모든 멤버변수를 출력하게끔 했기 때문에 입력값이었던 kim과 paySalary 메소드가 작동하여 4000/12+2000에 대한 결과값이 출력된다.
package com.acompany.inheritance;
public class InheritanceTest {
public static void main(String[] args) {
/*Employee emp = new Employee("kim", 5000);
System.out.println(emp);*/
//=> 오브젝트 클래스의 toString() 메소드가 호출되며 주소값이 출력되게 됨
// toString 메소드를 Employee 클래스에서 오버라이딩해서 재정의한 후 다시 프린트하면 name과 salary가 출력됨.
Manager mgr = new Manager("kim", 4000, 2000);
// Employee mgr = new Manager 해도 가능. 어차피 Manager 객체로 선언?기 때문에. (is-a). Manager가 Employee를 상속받았기 때문에.
System.out.println(mgr);
//=> 오브젝트 클래스의 toString() 메소드가 호출되며 주소값이 출력되게 됨
// Manager 클래스에서 toString()을 오버라이딩하게되면 Manager에서는 자신의 멤버변수인 bonus만 출력하는것으로 toString()을 정의했기 때문에 bonus만 출력됨.
// 하지만 Manager의 toString()에 super.toString()으로 부모클래스의 toString() 메소드까지 포함해준다면 name과 salary 모두 출력됨.
// 인터페이스의 static 메소드는 상속되는건 아니고 인터페이스에서 바로 스태틱 메소드 이름으로 접근하면 된다.
Payable.testStatic();
}
}
'Programming > Java' 카테고리의 다른 글
[Java9 프로그래밍] 12. 람다 표현식 (0) | 2020.03.21 |
---|---|
[Java9 프로그래밍] 11. 인터페이스 (0) | 2020.03.21 |
[Java9 프로그래밍] 09. 자바 객체지향 - more (0) | 2020.03.19 |
[Java9 프로그래밍] 08. 객체지향 모델링 (0) | 2020.03.13 |
[Java9 프로그래밍] 07. 함수(메소드) (0) | 2020.03.13 |