이번 포스팅에서는 상속과 다형성에 대해 정리하고, 필요한 관련 개념들을 간단히 정리할 것입니다.
목차는 아래와 같습니다.
- 상속 - super() 생성자
- 업 캐스팅
- 정적바인딩과 동적바인딩
- 다운 캐스팅
상속 - super() 생성자
상속된 서브 클래스의 객체가 main함수에서 생성되면 자동적으로 서브클래스의 기본생성자를 호출하고,
서브클래스에서는 슈퍼클래스의 기본 생성자를 호출하여 차례대로 실행이 된다.
클래스A, B, C 가 있고 B가 A를, C가 B를 상속한 코드로 보면 이렇게 된다.
그리고 이를 실행해보면, 다음과 같은 결과가 뜨는데,
A클래스의 객체를 생성했을땐 A의 기본생성자가 호출되고,
B클래스의 객체를 생성했을땐 B의 기본생성자 호출-> 부모인 A의 기본생성자 호출-> A의 기본생성자 먼저 실행.
C클래스의 객체를 생성했을땐 C의 기본생성자 호출->부모인 B의 기본생성자 호출-> 부모인 A의 기본생성자 호출->A,B,C 순으로 실행.
근데 만약 기본 생성자가 없다면? 오류가 발생한다.
만약 C의 매개변수있는 생성자를 호출한다면?
main에서 C c = new C(5); 로 c를 생성한다면, C의 매개변수 생성자 호출 -> B의 기본생성자 호출 -> A의 기본생성자 호출 -> A의 기본생성자 실행 -> B의 기본생성자 실행 -> C의 매개변수 생성자 실행
이때, 서브클래스에서 명시적으로 슈퍼클래스의 생성자를 선택 호출할 수 있다. super()를 통해.
But, 서브클래스 생성자코드의 제일 첫 라인에 와야한다!
만약 부모 클래스에 기본 생성자가 없다면 필수로 작성해줘야하는 것이다! (그렇지않으면, 오류발생)
위 코드에서 클래스 B의 매개변수생성자 아래에 super(x); 를 넣어주고 클래스 A에 매개변수생성자를 써준다면, 코드 실행 시, 클래스 A의 매개변수생성자가 호출될 것이다.
즉, 이러한 코드와 결과가 나타난다.
main에서 각각 클래스의 객체를 생성해주면, 생성자가 호출된다. 마지막 객체c 생성을 주목해야하는데, 보통이면 (객체 b가 생성된 결과를 보면) 부모클래스의 기본생성자를 호출하지만, C클래스의 매개변수 생성자가 호출되면서 그속에있는 super(a);에 의해 B클래스의 매개변수 생성자가 호출되게 된다.
업 캐스팅
업 캐스팅이란 서브 클래스의 객체가 슈퍼 클래스 타입으로 형변환되는 것을 의미한다.
아래의 코드는 Person, Student 클래스가 있고, Student는 Person클래스를 상속받는 서브클래스이다.
public class Person {
String id;
String name;
Person(String name) {
this.name = name;
}
}
public class Student extends Person {
int grade;
String department;
Student(String k) {
super(k);
grade=60;
}
}
public class Upcasting {
public static void main(String[] args) {
Student st = new Student("홍길동");
System.out.println(st.name);
System.out.println(st.grade);
Person p; //아무 곳도 가리키지 않는 null값
p = st; //사실은 p = (Student)st; 명시적으로 해주지 않아도 괜찮. Student 객체를 가리키는 Person타입 레퍼런스 변수.
//p.grade; 하면 오류. 업캐스팅을 한 이상, 자신Person의 멤버에만 접근할 수 있음.
}
}
Person은 Student보다 큰 범주의 클래스인데,
Person p = new Student(); 는 클래스 Student()를 객체화해서 클래스 Person 타입의 변수에 넣어준 것이다. 즉 의미는 Student 클래스의 객체가 Person 클래스의 객체인 것처럼 동작할 수 있다고 볼 수 있는 것인데, 즉, p는 Person 클래스의 멤버에 접근할 수 있다는 것이다. 반면, 그렇기 때문에 Student 클래스의 객체에 접근하게 되면 오류가 발생한다. 코드에서의 p = st;도 이와 같은 맥락이다.
하나의 코드를 더 보자면,
class A {
public String x() { return "A.x"; }
}
class B extends B {
public String x() { return "B.x"; }
//오버라이딩. 슈퍼 클래스의 메서드를 오버라이딩하게되면, x()를 호출했을때 무조건 하위클래스의 메서드가 실행된다.
public String y() { return "y"; }
}
public class Upcasting1 {
public static void main(String[] args) {
A obj = new B(); //B의 인스턴스가 A타입인 변수에 들어간 것. 즉, B의 인스턴스가 A의 행세를 하는것.
System.out.println(obj.x()); //B 클래스의 x()가 호출.
// System.out.println(obj.y()); 오류. B 클래스의 메서드인 y()에는 접근이 안됨.
}
}
B 클래스의 x()에는 접근할 수 있지만 y()에는 접근할 수 없다. 왜일까?
x()는 슈퍼 클래스 A의 클래스를 오버라이딩한 것이다. 즉, obj.x()로 A 클래스의 x()를 호출했지만,
오버라이딩의 특성상(오버라이딩된 함수가 호출되는 특성) B 클래스의 x()가 호출되지만, B 클래스의 메서드인 y()에는 접근할 수 없는 것이다.
만약 다형성이 없었다면? 우리는 같은 기능의 메서드를 여러번 반복해야하는 불편함을 겪어야 된다.
*오버라이딩 이란 ?
슈퍼클래스의 메소드를 서브클래스에서 중복 작성하는 것이라고 할 수 있는데, 슈퍼 클래스의 메소드 무력화, "메소드 무시하기"로 번역되기도 한다. 즉, 항상 서브 클래스에 오버라이딩한 메소드가 실행되도록 보장된다.
오버라이딩 조건 : 슈퍼 클래스 메소드의 원형(메소드 이름, 인자 타입 및 개수, 리턴 타입) 동일하게 작성.
정적바인딩과 동적바인딩 (결정되는 시점이 다른 것도 큰 차이점이다.)
정적바인딩은 메소드 오버로딩에, 동적바인딩은 메소드 오버라이딩에서 쓰이는 바인딩이다. 보통 정적바인딩은 생성자에 관하여 쓰이는데, 결국 동적바인딩은 오버라이딩된, 즉 서브클래스의 메서드가 우선 호출되는 것이고, 정적바인딩은 수퍼 클래스의 메서드를 호출하는 것이 차이점이다. 하나의 코드로 예를 보자면,
class SuperObject {
protected String name;
public void paint() {
draw(); //동적바인딩. 서브클래스의 draw()가 실행됨.
}
public void draw() {
System.out.println(name);
}
}
public class SubObject extends SuperObject {
protected String name;
public void draw() { //메서드 오버라이딩.
name = "Sub";
super.name = "Super"; //정적바인딩. super 는 SuperObject클래스를 의미.
super.draw(); //정적바인딩.
System.out.println(name);
}
public static void main(String[] args) {
SuperObject b = new SuperObject(); //SuperObject 클래스의 인스턴스.
b.paint();
}
}
결국 b.paing(); 의 코드를 실행하게 되면
정적 바인딩으로 슈퍼클래스의 paint()가 실행되지만, 동적바인딩으로 서브클래스의 draw()가 실행됨.
다운캐스팅
다운캐스팅이란 업 캐스팅된 (서브클래스의) 객체를 다시 돌아오게 하는 것이다.
하지만 다운 캐스팅은 명시적으로 형변환해줘야하기 때문에 객체의 기존 타입을 알아야 한다.
즉, 위의 코드에서 p = st; 를 다시 다운캐스팅한다면, st = (Student)p; 로 해줘야 한다.
만약 기존 타입을 알지못한다면 instanceof 를 통해 알수 있다.
instanceof
업캐스팅 된 객체의 (업캐스팅하기전)원상태를 알수 없을 경우, 사용하는 문법이다.
[사용법] [객체] instanceof [예상되는원상태]
ex) p instanceof Person => 맞으면 true 출력, 틀리면 false 출력.
'언어 > Java' 카테고리의 다른 글
기초 #6. 인터페이스 예습 (0) | 2020.08.23 |
---|---|
기초 #5. 추상클래스 (ex. 계산기 프로그램) (0) | 2020.08.22 |
기초실습 #2. 학생 점수 프로그램, 차량 관리 프로그램 (0) | 2020.08.16 |
기초실습 #1. 계좌이체 프로그램 예제 (0) | 2020.08.14 |
기초 #3 변수 (0) | 2019.06.28 |