자바에서의 상속(inheritance)
1.자바 상속의 특징
상속이란 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미한다. 상속은 캡슐화, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나이다 .
상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아 ,새로운 클래스를 생성할 수 있다 . 이때 기존에 정의되어 있던 클래스를 부모 클래스(parent class) 또는 상위클래스 (super class), 기초 클래스 (base class)라고도 한다 . 상속을 통해 새롭게 작성되는 클래스를 자식클래스(child class) 또는 하위 클래스 (sub class), 파생 클래스 (derived class) 라고 한다.
상속의 장점
자바에서 클래스의 상속은 다음과 같은 장점을 가진다 .
- 기존에 작성된 클래스를 재활용할 수 있다 .
- 자식 클래스 설계 시 중복되는 멤버를 미리 부모클래스에 작성해 놓으면 ,자식 클래스에서는 해당 멤버를 작성하지 않아도 된다.
- 클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련한다 .
상속은 확장판이다 .
자식클래스(child class)
자식 클래스란 부모 클래스의 모든 특성을 물려받아 새롭게 자성된 클래스를 의미한다.
자바에서 자식 클래스는 다음과 같이 선언한다.
class Test1 {
public String a = "1의 a";
public String b = "1의 b";//<--부모 클래스
public String c= "1의 c";
}
class Test2 extends Test1{
public String a ="2의 a";
public String b ="2의 b"; //<--자식클래스
}
//http://www.tcpschool.com/에서 참조
위와 같이 부모 클래스는 자식 클래스에 포함되 있다 . 부모클래스에 새로운 필드를 추가하면 자식 클래스에도 자동으로 해당 필드가 추가된 것처럼 동작한다.
자식클래스에는 부모 클래스의 필드와 메소드만이 상속되며 ,생성자와 초기화 블록은 상속되지 않는다 , 또한 부모 클래스의 접근 제어자가 private이거나 default로 설정된 멤버는 상속은 받지만 접근할 수 없다 . 자바에서 클래스는 한개의 클래스만을 상속받는 단일 상속만 가능하다.
2.super 키워드
super 키워드는 부모 클래스로부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 변수이다 . 인스턴스 변수의 이름과 지역 변수의 이름이 같을 경우 인스턴스 변수 앞에 this를 사용하여 구분한 것 처럼
자식 클래스의 멤버 이름이 부모클래스의 멤버 이름과 같을 경우 super 키워드를 사용하여 구별할 수 있다 .
class Test1 {
public String a = "1의 a";
public String b = "1의 b";//<--부모 클래스
public String c= "1의 c";
}
class Test2 extends Test1{
public String a ="2의 a";
public String superA=super.a;
public String b ="2의 b"; //<--자식클래스
}
public class test {
public static void main(String[] args) {
Test2 test2 = new Test2();
System.out.println(test2.a);
System.out.println(test2.superA);
}
}
결과
2의 a
1의 a
3.super() 메소드
this() 메소드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면 , super() 메소드는 부모 클래스의 생성자를 호출할 때 사용된다 .
자식 클래스의 인스턴스를 생성하면 ,해당 인스턴스에는 자식 클래스의 고유 멤버뿐만 아니라 부모 클래스의 모든 멤버까지도 포함되어 있다.
때문에 부모 클래스의 멤버를 초기화하기 위해서는 자식 클래스의 생성자에서 부모 클래스의 생성까지 호출해야만 한다.
이러한 부모 클래스의 생성자 호출은 모든 클래스의 부모 클래스인 Object 클래스의 생성자까지 계속 거슬러 올라가며 수행된다 .
따라서 자바 컴파일러는 부모 클래스의 생성자를 명시적으로 호출하지 않는 모든 자식 클래스의 생성자 첫 줄에 자동으로 다음과 super(); 명령문을 추가하여, 부모 클래스의 멤버를 초기화 할 수 있도록 한다 .
하지만 자바 컴파일러는 컴파일 시 클래스에 생성자가 하나도 정의되어 있지 않아야만 ,자동으로 기본 생성자를 추가해 준다 . 만약 부모 클래스에 매개 변수를 가지는 생성자를 하나라도 선언했다면 , 부모 클래스에는 기본 생성자가 자동으로 추가되지 않을 것이다 .
4.메소드 오버라이딩(method overriding)
먼저 알아봤던 오버로딩은 서로 다른 시그니처를 갖는 여러 메소드를 하나의 이름으로 정의하는 것이였지만.
오버라이딩이란 상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것이다
자바에서 자식 클래스는 부모 클래스의 private 멤버를 제외한 모든 메소드를 상속받는다. 이렇게 상속받은 메소드는 그대로 사용해도 되고 , 필요한 동작을 위해 재정의 하여 사용할 수도 있다 .
즉, 메소드 오버라이딩이란 상속받은 부모 클래스의 메소드를 재정의하여 사용하는 것을 의미한다.
-오버라이딩의 조건
1.오버라이딩이란 메소드의 동작만을 재정의하는 것이므로 , 메소드의 선언부는 기존 메소드와 완전히 같아야 한다. 하지만 메소드의 반환 타입은 부모 클래스의 반환 타입으로 타입을 변환할 수 있는 타입이라면 변경이 가능하다 .
2.부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다 .
3.부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다 .
class Test1 {
public String a ;
public String b ;
public String c ;
public Test1(String a, String b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
}
class Test2 extends Test1{
public String d;
public String e;
public String f;
public Test2(String a, String b, String c, String d, String e, String f) {
//부모 클래스에 기본생성자가 없기때문에 자식인 Test2는 부모의 생성자까지 초기화해야한다.
super(a, b, c);
this.d = d;
this.e = e;
this.f = f;
}
//부모 클래스에 생성자가 없다면 컴파일러는 자동으로 부모클래스에 기본생성자를 만들것이고
//자식 클래스에서는 자동으로 부모의 기본생성자를 가져오는 super() 사용한다.
}
public class test {
public static void main(String[] args) {
Test2 test2 = new Test2("3","2","1","4","5","6");
System.out.println(test2.d);
}
}
5.다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
다형성 polymorphism
다형성은 many라는 뜻의 poly와 form이라는 뜻의 morph의 조합이다 다향한 형태를 의미하며 자바에서는 동일한 네이밍을 가지고 여러 형태의 동작을 하는 테크닉을 의미한다. static method overloading 을 통해 컴파일 떄의 다형성을 실현할 수 있고 , overrriding을 통하 런타임 시 다형성을 실현할 수 있다 .
런타입 다형성이 일반적으로 말하는 다형성이며 , 컴파일 타임이 아닌ㄴ , 런타임에 upcasting된 자식 클래스의 오버라이딩된 메소드를 호출한다. 실행 시간에 어떤 자식클래스의 오버라이딩 메소드를 호출할지가 명확해지기 떄문이다. 실행시간 다형성의 다른말이 dynamic method dispatch이다 .
dynamic method dispatch
dynamic은 runtime의 동의어로 사용되며 ,dispatch는 어떤 메소드를 호출할지 결정하는 것이다 .
아래의 코드로 dynamic method dispatch를 설명 하겠다 .
//A1, A2는 A클래스를 extend 했다.
//A1, A2는 A클래스의 printHi를 overriding 했다.
class A{
void printHi(){
System.out.println("hi i am A");
}
}
class A1 extends A{
@Override
void printHi() {
System.out.println("hi i am A1");
}
}
class A2 extends A{
@Override
void printHi() {
System.out.println("hi i am A2");
}
}
public class DynamicDispatchTest {
public static void main(String[] args) {
A a = new A();
a.printHi();
a = new A1();
a.printHi();
a = new A2();
a.printHi();
}
}
위의 코드를 살펴보면 메인메서드에서
1.A 인스턴스를 생성하고 A 타입 a 변수에 담았은 후에 , printHi()를 실행했다.
2.다음으로 A 타입의 변수 a에 A1의 printHi()를 실행
3.다음으로 A 타입의 변수 a에 A2의 printHi()를 실행
2,3번에서는 부모타입인 A로 upcasting이 일어난다 . 이때 A 타입 변수 a의 레퍼런스는 자식 객체가 대입될 때마다 .
dynamic method dispatch을 통해 자식 객체의 주소를 가리키게 된다.
그러므로 위 실행의 결과는 아래와 같다 .
hi i am A
hi i am A1
hi i am A2
참조 https://velog.io/@maigumi/Dynamic-Method-Dispatch
'JAVA' 카테고리의 다른 글
JAVA 스터디 10 - final (0) | 2021.03.18 |
---|---|
JAVA 스터디 9 - 추상 클래스와 추상메서드 (0) | 2021.03.18 |
JAVA 스터디 7 - 자료구조 LinkedList , Stack , Queue (0) | 2021.02.13 |
JAVA 스터디 6 - JUNIT5 (0) | 2021.02.08 |
JAVA 스터디 5 - switch expression (자바 12부터 추가됨) (0) | 2021.02.05 |