-
백기선님의 Java 스터디를 진행하며 찾아본 내용입니다.
목표
자바의 상속에 대해 학습하세요.
학습할 것
- 자바 상속의 특징
- super 키워드
- 메소드 오버라이딩
- 다이나믹 메소드 디스패치(Dynamic Method Dispatch)
- 추상 클래스
- final 키워드
- Object 클래스
자바 상속의 특징
- Object 클래스를 제외하고 모든 클래스는 단 하나의 direct super class가 존재한다.
- 명시적인 super 클래스가 없다면 모든 클래스는 암시적으로 Object의 서브 클래스이다.
super 키워드
메소드가 슈퍼 클래스의 메소드중 하나를 오버라이드 할 경우 super 키워드를 이용해 재정의 되어진 메소드를 call 할 수 있다.
다음과 같이 슈퍼클래스와 Subclass가 있다고 할 때
123456public class Superclass {public void printMethod() {System.out.println("Printed in Superclass.");}}cs 12345678910111213public class Subclass extends Superclass {// overrides printMethod in Superclasspublic void printMethod() {super.printMethod();System.out.println("Printed in Subclass");}public static void main(String[] args) {Subclass s = new Subclass();s.printMethod();}}cs 결과는 다음과 같이 나온다.
Printed in Superclass. Printed in Subclass
또한 Bicycle이라는 클래스를 상속받는 MountainBike라는 클래스가 있다고 할 때
다음과 같이 super 키워드를 사용해 생성자에 값을 전달 해 줄수 있다.
1234567public MountainBike(int startHeight,int startCadence,int startSpeed,int startGear) {super(startCadence, startSpeed, startGear);seatHeight = startHeight;}cs 슈퍼클래스의 생성자를 호출하는 경우 다음 문법을 따른다.
super();
or
super(parameter list)
* super 클래스의 생성자 호출은 반드시 서브클래스 생성자의 첫번째 라인에서 진행되어져야 한다.
* 생성자에서 슈퍼클래스 생성자를 명시적으로 호출하지 않을 경우 자바 컴파일러는 슈퍼 클래스의 인수가 없는 생성자에 대한 호출을 자동으로 실시하는데 이 때, 인수가 없는 생성자가 없다면 컴파일러 에러가 발생되어진다.
* 부모 생성자를 명시적으로든 암묵적으로든 호출 할 때 Object에 도달할 때 까지의 모든 상위 클래스의 생성자가 호출되어지는데 이를 'constructor chaining'이라고 한다.
메소드 오버라이딩
슈퍼 클래스의 메소드를 서브 클래스에서 재정의해서 사용하는 것을 오버라이딩이라고 한다.
이때 오버라이딩 하는 메소드는 슈퍼클래스의 메소드와 이름, 파라미터의 타입, 리턴타입이 같아야 한다.
자바 5부터는 리턴 타입을 슈퍼클래스의 메소드의 리턴타입의 서브타입으로 리턴 할 수 있게 되었는데 이를 covariant return type이라고 한다.
만약 서브 클래스가 슈퍼클래스의 static 메소드와 같은 시그니쳐(리턴타입, 이름, 파라미터 타입 등)를 가진 static 메소드를 정의하려고 할 때, 이 때 서브 클래스의 메소드는 슈퍼 클래스의 메소드를 'hiding'한다.
정적 메소드 hiding과 인스턴스 메소드를 override하는 것은 다음과 같은 영향을 미친다.
오버라이딩 되어진 메소드를 호출 할 때, 호출되는 메소드는 하위 클래스에 있는 메소드이다.
hiding된 static 메소드를 호출 할 때, 호출되는 메소드는 해당 메소드가 슈퍼클래스에서 호출되는지 아니면 서브클래스에서 호출되는지의 여부에 따라 달라진다.
12345678public class Animal {public static void testClassMethod() {System.out.println("The static method in Animal");}public void testInstanceMethod() {System.out.println("The instance method in Animal");}}cs 123456789101112131415public class Cat extends Animal {public static void testClassMethod() {System.out.println("The static method in Cat");}public void testInstanceMethod() {System.out.println("The instance method in Cat");}public static void main(String[] args) {Cat myCat = new Cat();Animal myAnimal = myCat;Animal.testClassMethod();myAnimal.testInstanceMethod();}}cs 위와 같은 코드에서 결과는 다음과 같이 나온다.
The static method in Animal The instance method in Cat
다이나믹 메소드 디스패치(Dynamic Method Dispatch)
다이나믹 메소드 디스패치는 compile time이 아니라 run time에 오버라이딩이 된 메소드가 호출되어지는 방식이다.
추상(abstract)
추상메소드
다음과 같이 구현 없이 선언된 메소드를 의미한다.
추상클래스
추상 클래스란 abstract 키워드를 통해 선언되어진 클래스를 의미한다.
A클래스, B클래스, C클래스가 있다고 할 때
A클래스, B클래스, C클래스들간에 비슷한 필드와 메서드를 공통적으로 추출해서 만들어진 클래스이다.
실체 클래스는 실체가 드러나는 클래스이고
추상 클래스는 실체 클래스들의 공통적인 부분을 추출해 어느정도 규격을 잡아놓은 클래스이다.
실체 클래스는 실제 객체를 생성할 정도의 구체성을 가지는 반면 추상 클래스는 아직 메소드와 내용이 추상적이기 때문에 객체를 생성할 수 없게 만들었다.
여기서 추상 클래스와 실체 클래스는 상속적인 관계를 가지고 있다.
- 추상 클래스는 인스턴스화 되어질 수 없지만 상속을 시켜줄 수는 있다.
- 추상 메소드를 포함할수도, 안 할 수도 있다.
- 만약 추상 메소드를 클래스가 포함한다면 클래스는 반드시 abstract 키워드를 이용해 선언되어져야 한다.
- 추상 클래스가 선언되어 질 때 서브클래스는 일반적으로 부모 클래스의 모든 추상 메소드의 구현을 제공해준다.
- 그러나 만약 이렇게 하지 않는다면 서브 클래스 또한 abstract를 이용해 선언되어져야 한다.
다음은 추상 클래스 선언의 예시이다.
12345public abstract class GraphicObject {// declare fields// declare nonabstract methodsabstract void draw();}cs 왜 사용하는가?
3가지 이유가 있다.
- 공통된 필드와 메소드를 통일할 목적
- 실체 클래스 구현시, 시간을 절약 할 수 있다.
- 규격에 맞는 실체클래스를 구현 할 수 있다.
추상 클래스에 대해서 다음 블로그의 내용을 많이 참고했습니다.
https://limkydev.tistory.com/188?category=957527
final 키워드
final 키워드 사용하는 곳 효과 method 오버라이딩을 금지한다 Object 변경될 수 없고, 확장 할 수 없다. Field 값을 변경 할 수 없도록 한다(상수화 한다.) * 생성자에서 호출되어지는 메소드는 final로 선언하는 것이 좋다. sub-class에서 메소드를 수정해 원하지 않는 결과가 나올 수 있기 때문이다.
* 전체 클래스를 final로 선언하면 subclass를 만들 수 없도록 할 수 있다.
Object 클래스
클래스 계층의 root 클래스
Method Discription protected Object clone() 이 객체의 복사본을 생성하고 리턴한다. boolean equals(Object obj) 다른 객체가 호출한 객체와 같은지의 여부를 리턴한다. protected void finalize() 객체에 대한 참조가 없을 때 가비지 컬렉터에 의해 호출되어진다. Class<?> getClass() 이 객체의 런타임 클래스를 리턴한다. int hashCode() 객체의 해시 코드 값을 리턴한다. void notify() 이 객체의 모니터에서 대기중인 단일 스레드를 깨운다 void notifyAll() 이 객체의 모니터에서 대기중인 모든 스레드를 깨운다 void toString() 객체의 문자열 표현을 리턴한다. void wait() 다른 스레드가 이 객체에 대해 notify() 또는 notifyAll() 메소드를 호출 할 때 까지
현재 스레드가 대기하도록 한다.void wait(long timeout) 현재 스레드가 notify()혹은 notifyAll() 메소드에 의해 호출되어지거나 입력된 시간이 지날 때 까지 대기하도록 한다. void wait(long timeout, int nanos) 현재 스레드가 notify()혹은 notifyAll() 메소드에 의해 호출되어지거나 다른 스레드가 현재 스레드를 인터럽트하거나 입력된 시간이 지날 때 까지 대기하도록 한다. Reference
docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
docs.oracle.com/javase/tutorial/java/IandI/super.html
docs.oracle.com/javase/tutorial/java/IandI/override.html
www.geeksforgeeks.org/dynamic-method-dispatch-runtime-polymorphism-java/
docs.oracle.com/javase/tutorial/java/IandI/abstract.html
docs.oracle.com/javase/tutorial/java/IandI/final.html
docs.oracle.com/javase/7/docs/api/java/lang/Object.html#Object()