본문 바로가기
Language/Java

JAVA 6주차 : 상속

by CleanCoder 2021. 2. 13.

목표

자바의 상속에 대해 학습하세요.

학습할 것 (필수)

  • 자바 상속의 특징
  • super 키워드
  • 메서드 오버라이딩
  • 다이내믹 메서드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

 

1. 자바 상속의 특징

 

 부모가 자식에게 재산을 물려주는 상속과 같이 자바에서도 부모 클래스의 멤버를 자식 클래스로 물려줄 수 있다.

부모 클래스를 상위 클래스라고 부르기도 하며, 자식 클래스를 하위 클래스, 파생 클래스라고도 부른다.

 

상속은 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.

 

A 클래스를 B 클래스가 상속받는다 하면 B에서 A의 필드와 메서드를 사용할 수 있는 것이다.

 

다만 주의할 점은 부모의 private 접근 제한을 갖는 필드와 메서드는 상속 대상에서 제외된다.

 

그리고 부모 클래스와 자식 클래스가 다른 패키지에 존재한다면 default 접근 제한을 갖는 필드와 메서드도 상속 대상에서 제외된다.

 

다음과 같이 자식 클래스에 extends 뒤에 기술하는 방식으로 상속을 받는다.

class 자식클래스 extends 부모클래스{

	//필드
	//생성자
	//메서드
}
   

 

다른 언어와의 차이점은 자바에서는 다중 상속을 허용하지 않는다는 점이다. 여러 개의 부모 클래스를 상속할 수 없다. 단 하나의 부모 클래스만 와야 한다는 말이다.

 

 

2. super 키워드

 

 다음의 객체 생성은 Student 객체만 생성하는 것처럼 보이지만 사실은 부모인 Person 객체가 생성되고, Student 객체가 생성된다.

Student student = new Student();

 

모든 객체는 클래스의 생성자를 호출해야만 생성된다. 부모 생성자는 자식 클래스의 맨 첫 줄에서 호출된다. 예를 들어 Student의 생성자가 명시적으로 선언되지 않았다면 컴파일러는 다음과 같은 기본 생성자를 생성해낸다.

public Student(){
	super();
}

 

여기서 super()는 부모의 기본 생성자를 호출한다. 즉 Person 클래스의 다음 생성자를 호출한다.

public Person(){
}

 

Person.java 소스 코드에 Person 생성자가 선언되지 않았지만 컴파일러에 의해 기본 생성자가 만들어지므로 문제없이 실행된다. 

 

자식이 직접 생성자를 선언하고 명시적으로 부모 생성자를 호출하고 싶다면 다음과 같이 작성할 수 있다.

자식클래스(매개변수선언, ...){
	super( 매개값, ...);
}

다음은 super의 매개 값의 타입과 일치하는 부모 생성자를 호출한다. 만일 매개값의 타입과 일치하는 부모 생성자가 없을 경우에는 컴파일 오류가 발생한다.

 

super(매개값, ...) 가 생략되면 컴파일러에 의해 자동적으로 super()가 추가되기 때문에 부모의 기본 생성자가 존재해야 한다. 부모 클래스에 기본 생성자가 없고 매개 변수가 있는 생성자만 있다면 자식 생성자에서 반드시 부모 생성자 호출을 위해 super(매개값,...)를 명시적으로 첫 줄에 호출해야 한다.

 

 

생성자 외에도 super를 통해서 부모의 메서드를 호출할 수 있다. 자식 클래스에서 부모 클래스의 메서드를 오버라이딩 할 시 부모 클래스의 메서드는 숨겨지고 자식 클래스의 메서드만 사용되기 때문에 부모 클래스의 메서드를 호출하려면 super를 붙여서 부모 메서드를 호출할 수 있다.

 

부모 클래스

class Person{
	void method1(){}
	void method2(){}
}

자식 클래스

class Student extends Person{
	void method2(){}	//Overriding
	void method3(){
		method2();
		super.method2();
	}
}

자식 클래스에서 method2가 재정의되었기에 부모의 method2를 호출하기 위해 super 키워드를 사용

 

 

3. 메서드 오버라이딩

 

 메서드 오버라이딩은 상속된 메서드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메서드를 재정의 하는 것을 의미한다. 메서드가 오버라이딩 되었다면 부모 객체의 메서드는 숨겨지기 때문에, 자식 객체에서 메서드를 호출하면 오버라이딩 된 자식 메서드가 호출된다.

 

메서드 오버라이딩 시 주의할 점

  • 부모의 메서드의 동일한 시그니처(리턴 타입, 메서드 이름, 매개 변수 리스트)를 가져야 한다.
  • 접근 제한을 더 강하게 오버라이딩할 수 없다.
  • 새로운 예외(Exception)를 throws 할 수 없다.

접근 제한을 더 강하게 오버라이딩할 수 없다는 것은 부모 메서드가 public 접근 제한을 가지고 있을 경우 오버라이딩하는 자식 메서드는 default나 private 접근 제한으로 수정할 수 없다는 뜻이다.

 

반대로 부모 메서드가 default 접근 제한을 가지면 재정의되는 자식 메서드는 default 또는 public 접근 제한을 가질 수 있다. 

 

부모 클래스

public class Calculator{
	double areaCircle(double r){
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return 3.14159 * r * r;
	}
}

 

자식 클래스

public class Computer extends Calculator{
	@Override
	double areaCircle(double r){
    	System.out.println("Computer 객체의 areaCircle 실행");
		return Math.PI * r * r;
	}
}

 

메서드 오버라이딩 테스트

public class ComputerExample{
	public static void main(String[] args){
	int r = 10;

	Calculator calculator = new Calculator();
	System.out.println("원 면적 : " + calculator.areaCircle(r));
	System.out.println();

	Computer computer = new Computer();
	System.out.println("원 면적 : " + computer.areaCircle(r));	//재정의된 메서드 호출
	}
}
더보기

Calculator 객체의 areaCircle() 실행

원 면적 : 314.159

 

Computer 객체의 areaCircle() 실행

원 면적 : 314.1592653589793

 

 

4. 다이나믹 메서드 디스패치 (Dynamic Method Dispatch)

 

 Dynamic method dispatch 에서 dynamic은 runtime의 동의어로 사용되며, dispatch는 어떤 메서드를 호출할지 결정하는 것이다.

 

다른 예로는 Static method dispatch 가 있으며 자바의 다형성을 알면 더 잘 이해할 수 있다.

 

다형성

 

 결국 상속과 다형성은 OPP를 구성하는 가장 중요한 키워드라고 할 수 있다. 이 다형성은 여러 가지 형태를 가질 수 있는 능력이고 자바에서는 한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 하는 것. 으로 자바에서는 다형성을 구현했다.

 

조금 더 가볍게 가자면, 내가 생각하기에는 부모가 자식 객체를 생성하면, 자식 클래스의 포함되어있는 자신의 변수와 메서드를 사용할 수 있지만, 실제로 자식의 메서드를 사용할 수는 없다.

 

그 반대로 자식이 부모 인스턴스를 생성할 수는 없다. 왜냐하면, 자식에는 훨씬 많은 메서드와 훨씬 많은 변수가 있기 때문에, 생성 자체가 되지 않는다.

 

메서드 디스패치는 이 과정에서 자바가 겪는 메서드를 어떤 것을 호출할지를 결정하는 과정을 말한다.

이 과정은 위의 말처럼 Dynamic ⇒ 동적일 수 도 있고, Static ⇒ 정적일 수도 있다.

 

일단 정적 메서드 디스패치컴파일 시점부터 우리가 특정 메서드를 실행시킬 줄 아는 경우를 뜻한다.

class A{
	void show(){
		System.out.println("1");
	}
}

public class main{
	public static void main(String[] args){
		A a = new A();
		a.show();
	}
}

 

이런 식으로 구성하게 되면, 컴파일 시 A 객체가 만들어져 있는 걸 확인했고 → 그에 포함돼있는 show라는 메소드를메서드를 사용하는 것까지 확인이 되므로 바로 정적으로 이 클래스에서 메서드를 실행하는 모습으로 실행시킬 것이다.

 

동적 메소드 디스패치컴파일 당시에는 정해져 있지 않지만, 런타임에 정해져서 호출하는 것을 동적 메서드 디스 패쳐라고 한다.

 

다음과 같은 예시를 들 수 있다.

 

class A{
	void show(){
		System.out.println("1");
	}
}

class B extends A{
	void show(){
		System.out.println("2");
	}
}

public class main{
	public static void main(String[] args){
		A a = new B();
		a.show();
	}
}

다음과 같이 A 객체는 B클래스의 인스턴스로 구성되어있지만, 실제로 show를 통해서 보여지는 값은 "2" 일 것이다.

 

이 경우 컴파일 시에는 확실하게 이것이 A인지 B로 보아야 할지는 모르는 상태이지만, 런타임 시 실행하면 메서드가 정해지는 경우를 우리는 동적 메서드 디스패치라고 할 수 있다.

 

 

5. 추상 클래스

 

 객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 하고, 이 클래스들의 공통적인 특성을 추출하여 선언한 클래스를 추상 클래스라고 한다.

 

추상 클래스가 부모이고 실체 클래스가 자식으로 구현되어 실체 클래스는 추상 클래스의 모든 특성을 물려받고 추가적인 특성을 가질 수 있다.

 

추상 클래스는 공통되는 필드와 메서드를 추출해서 만들었기 때문에 객체를 직접 생성해서 사용할 수 없다.

다시 말해서 추상 클래스는 new 연산자를 사용해서 인스턴스를 생성시키지 못한다.

 

이러한 추상 클래스를 만드는 이유

  • 실체 클래스들의 공통된 필드와 메서드의 이름을 통일할 목적
  • 실체 클래스를 작성할 때 시간을 절약

실체 클래스를 설계하는 사람이 여러 사람일 경우 실체 클래스의 필드와 메서드가 제작기 다른 이름을 가질 수 있음. 하여, 추상 클래스에 필드와 메서드를 선언하고 상속함으로써 이름을 통일시킬 수 있고 시간을 절약할 수 있음

 

추상 클래스 선언

public abstract class 클래스{
	//필드
	//생성자
	//메서드
}

 

추상 클래스 설계 시에 하위 클래스가 반드시 메서드의 실행 내용을 채워야 할 경우 abstract을 메서드에 선언하여 오버라이딩을 사용해 해결할 수 있다.

public abstract class Animal{
	public abstract void sound();
}

 

 

6. final 키워드

 

final 키워드는 클래스, 필드, 메서드 선언 시에 사용할 수 있다. final은 해상 선언이 최종 상태이고, 수정될 수 없음을 의미한다.

필드 선언 시에 final 값이 지정되면 초기값 설정 후 더 이상 값을 변경할 수 없다.

클래스에서는 final 키워드를 붙이면 클래스는 상속할 수 없는 클래스가 된다. 즉, final class는 부모 클래스가 될 수 없다.

 

final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스이다. String 클래스는 다음과 같이 선언되어있다.

public final class String{...}

이는 String 클래스를 자식 클래스에서 상속받을 수 없음을 의미한다.

 

메서드에서 final 키워드를 붙이면 메서드는 Overriding 할 수 없는 메서드가 된다. 부모 클래스를 상속해서 자식 클래스를 선언할 때부터 부모 클래스에 선언된 final 메서드는 자식 클래스에서 재정의될 수 없음을 의미한다.

 

 

7. Object 클래스

 

 Object는 자바의 최상위 클래스이다. 자바에서 상속은 필수이고, 모든 클래스는 자동적으로 Object 클래스를 상속받게 되어있다.

 

class abc{}

다음의 class 선언은 아래와 같다.

class abc extends Object{}

 

이러한 Object 클래스의 메서드는 다음과 같다.

 

protected Object clone() 해당 객체의 복제본을 생성하여 반환함.
boolean equals(Object obj) 해당 객체와 전달받은 객체가 같은지 여부를 반환함.
protected void finalize() 해당 객체를 더는 아무도 참조하지 않아 가비지 컬렉터가 객체의 리소스를 정리하기 위해 호출함.
Class<T> getClass() 해당 객체의 클래스 타입을 반환함.
int hashCode()  해당 객체의 해시 코드값을 반환함.
void notify()  해당 객체의 대기(wait)하고 있는 하나의 스레드를 다시 실행할 때 호출함.
void notifyAll() 해당 객체의 대기(wait)하고 있는 모든 스레드를 다시 실행할 때 호출함.
String toString() 해당 객체의 정보를 문자열로 반환함.
void wait() 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행할 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.
void wait(long timeout) 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행하거나 전달받은 시간이 지날 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.
void wait(long timeout, int nanos) 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행하거나 전달받은 시간이 지나거나 다른 스레드가 현재 스레드를 인터럽트(interrupt) 할 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.

 

 
참고 : 이것이 자바다
생활 코딩
 
 
 
 

'Language > Java' 카테고리의 다른 글

JAVA 8주차 : 인터페이스  (0) 2021.02.14
JAVA 7주차 : 패키지  (0) 2021.02.14
JAVA 5주차 : 클래스  (0) 2021.02.12
JAVA 4주차 : 제어문  (0) 2021.02.12
JAVA 3주차 : 연산자  (0) 2021.02.12

댓글