확장(Extends) == 상속(Inheritance)
자바에서 확장(상속)은 클래스와 클래스간의 관계를 의미하며 상속은 부모 클래스가 자식클래스에게 메서드나 필드를
물려 주는것을 의미한다. ( 상속을 하는 쪽의 클래스를 부모클래스, 상속을 받는 클래스를 자식클래스라고 말한다. )
자바에서 상속보다는 확장이라는 표현을 하는 것이 더 올바른 표현이다.
이유는 부모클래스가 진짜 부모로 만들어진 객체 클래스 자식클래스가 진짜 자식으로 만든 객체 클래스로 가정을 해보자
부모의 역할을 상속받는다고 자식클래스가 부모가 될 순 없다. 이 말은 상속이라는 것은 올바르지 않은 표현이며, 확장이라는 표현이 올바른 표현이 된다. 확장은 부모클래스에게 받은 것들을 자식클래스에서 확장하여 이용할 수 있는 것이다.
# 상속을 알기에 앞서
상속을 알기 위해선 자바 컴파일러가 자동으로 만들어주는 요소를 알아야한다.
상속을 알기 위해 대표적으로 자바 컴파일러가 자동으로 만들어주는 요소 2가지를 보여주겠다.
1. Object 상속
2. 기본생성자 and super() 키워드
public class Test extends Object { // Object 상속
public Test() { // 기본생성자
super(); // 기본생성자와 함께 자동으로 만들어지는 요소
}
}
자바에서 클래스를 만들면 기본적으로 Object클래스를 상속을 받게된다. 물론 처음에는 생략이 되어져서 만들어 진다.
하지만 컴파일러가 돌아가면서 자동으로 상속을 받게 한다. 그리고 기본생성자 또한 자바에서 자동으로 만들어 준다.
그리고 기본생성자에 자동으로 super()키워드 또한 자바 컴파일러가 자동으로 만들어 준다.
super 키워드?
1. super : 상속구조에서 부모를 가리킬때 사용하는 키워드이다. (부모 클래스 자체를 가리키는 키워드)
2. super() : 부모의 생성자를 호출해주는 키워드이다. ( 매개변수값 없을 시 기본생성자 호출 )
# 결국 super() 키워드가 생성자를 호출하므로 부모 메서드나 필드를 사용 할 수 있는 것이다.
# 주의 : 컴파일러는 오버로딩 되어있는 생성자가 있을 경우 기본생성자를 만들어 주지 않으므로, 매개변수 생성자를 통해
직접 super()를 통해 매개변수를 넣어줘야 컴파일 오류가 안난다.
( super() = new 부모() / super("강아지",12) = new 부모("강아지",12) ---> 이런셈이다.
1. 확장(상속)
이제 클래스의 확장(상속)을 받는 과정을 보도록하자.
public class Animal { // 부모클래스
}
public class Dog extends Animal { // 자식클래스
public Dog() { // 기본생성자 super() 또한 기본으로 생성(컴파일러)
super(); // 부모클래스 Animal 클래스 호출
}
}
자식클래스에서 extends키워드를 이용하면 컴파일 단계에서 컴파일러가 extends키워드를 보고 상속을 구분할 수 있다.
참고로 임의로 상속구조를 만들어서 이렇게 상속을 받게되면 자동으로 만들어지는 extends Object는 사라지게 된다.
그렇지만 부모클래스인 Animal은 extends Object 즉, Object클래스를 상속받고 있으니 자식클래스 또한 Object 클래스를
확장(상속)을 받고 있는 셈이다.
2. 확장(상속) 메서드
super()키워드를 통해 자식클래스는 부모의 메서드를 이용할 수 있게된다.
기본적으로 자식클래스를 생성하고 자식 클래스의 주소값을 담고 있는 참조변수를 통해 sound()라는 부모클래스에 있는 메서드를 호출하면 정상적으로 잘 나오는 걸 확인할 수 있다. 이게 가능한 이유는 Dog클래스의 생성자를 보면 알 수 있다.
자세히 보면 super()로 부모의 객체를 생성하고 있는 걸 볼 수 있다.
즉, 부모의 생성자를 호출하여 sound() 메서드를 가지고 오는걸 볼 수 있다. ( 부모의 메서드를 가질 수 있다. )
public class Animal { // 부모클래스
public void sound() {
System.out.println("동물소리")
}
}
public class Dog extends Animal { // 자식클래스
public Dog() {
super();
}
}
public class Main { // 메인클래스
public static void main(String[] args) {
Dog dog = new Dog(); // 자식클래스 생성
dog.sound(); // 결과 : "동물소리"
}
}
3. 확장(상속) / 메서드 오버라이딩(메서드 재정의)
앞에서 봤던 내용으로는 부모클래스의 메서드를 자식클래스는 받을 수 있다.
부모클래스에 메서드를 받은 자식클래스는 그 부모클래스의 메서드를 재정의해서 다른 용도로 사용하는 것이 가능하다.
즉, 부모클래스의 메서드를 자식클래스가 동일한 메서드명으로 덮어버리는 것이다. 자식클래스로 호출을 했기때문에
결국 자식클래스의 메서드가 우선순위를 갖는다고 생각하면 될 것 같다. ( sound() 결과 => "멍멍" )
그러면 반대로 부모 클래스를 호출하게 되면 sound()의 결과는 "동물소리"가 나올 것이다.
public class Animal { // 부모클래스
public void sound() {
System.out.println("동물소리")
}
}
public class Dog extends Animal { // 자식클래스
public Dog() {
super();
}
@Override // override는 생략이 가능하지만 어떤걸 재정의 했는지 알려주기 위해 사용해야한다.
public void sound() {
System.out.println("멍멍")
}
}
public class Main { // 메인클래스
public static void main(String[] args) {
Dog dog = new Dog(); // 자식클래스 생성
dog.sound(); // 결과 : "동물소리"
}
}
3. 확장(상속) / 필드
기본적으로 상속에선 부모클래스의 필드는 자식 또한 받을 수 있지만 자식클래스에서 재정의하는 것은 불가능하다.
대신에 자식클래스에서 동일한 이름의 필드를 새롭게 정의를하면 그 필드는 부모 클래스의 필드를 가리게 되면서 이것은
필드 가리기 또는 필드 오버라이딩 이라고 한다. 즉, 필드를 가린다는 의미는 오버라이딩처럼 덮어 씌우는게 아닌 동일한
이름의 필드를 하나더 만들어서 자식만이 가지고 있다는 의미이다. 부모 클래스의 필드와 자식클래스의 필드는 독립적인 관계가 된다는 의미이다.
# 다형성이 적용 즉, 부모클래스 타입으로 받게 된다면 부모클래스의 필드의 값만이 호출 되므로 메서드 재정의랑 다르다.
# 다형성이 적용된 메서드 호출은 재정의된 자식메서드를 우선순위로 호출한다.
public class Animal { // 부모클래스
String name = "동물"; // 필드 선언
}
public class Dog extends Animal { // 자식클래스
String name = "강아지";
public Dog() {
super();
}
}
public class Main { // 메인클래스
public static void main(String[] args) {
Dog dog = new Dog(); // 자식클래스 생성
dog.name; // 결과 : 강아지
// 하지만 재정의를 통해 덮어씌운게 아니고 단순히 부모의 필드를 가린 것 이다.
}
}
3. 확장(상속) / 생성자
부모클래스의 매개변수 생성자가 존재할 경우 자식클래스에서도 동일하게 매개변수 생성자를 만들어줘야 한다.
그 이유는 앞서 말했던 super()키워드는 생성자를 호출하는 것 이기 때문에 부모 생성자 호출할 때 매개변수 생성자가
존재한다면 기본생성자는 컴파일러가 만들어 주지 않기 때문에 직접 super()키워드 안에 매개값을 넣어줘야 한다.
public class Animal { // 부모클래스
String name;
// 컴파일러는 오버로딩된 생성자가 존재할 경우 기본생성자를 만들어 주지 않는다.
// 즉, 필요하면 직접 만들어야함.
public Aniaml(String name) {
this.name = name;
}
}
public class Dog extends Animal { // 자식클래스
public Dog(String name) {
super(name);
}
}
public class Main { // 메인클래스
public static void main(String[] args) {
Dog dog = new Dog("강아지"); // 자식클래스 생성
}
}
상속의 포함관계 및 확장 관계
( 확장관계 / 포함관계 : 클래스 간의 관계맺기는 논리적으로 잘 판단해야한다. )
확장(상속)관계 / 포함관계
자동차 클래스 => 차문 클래스 포함관계
자동차 클래스 => 차종(아우디)클래스 상속관계
자동차 클래스에서 엔진과 같은 자동차의 상속관계가 아닌 클래스는 포함관계를 띄고있다.
엔진같은 경우에도 종류가 다양하기 때문에 엔진을 상속받는 다른 엔진이 존재?
참고 ( Object 클래스 )
자바에서 모든 클래스는 Object클래스를 상속받는다 이건 필수이기에 자바에서 굳이 extends를 명시를 안해도
컴파일러가 알아서 넣어준다. ( 참고로 자바는 단일 상속만을 허용한다. 다중 상속이 불가능하다. )
그럼 여기서 생각해 볼 수 있다. 모든 클래스들은 Object를 상속받는데 만약 내가 다른 클래스를 상속 받게 된다면?
그럼 Object의 상속을 끊고 다른 클래스의 상속을 받는 것일까? 직접 확인을 해보니 상속을 끊는 것이 맞았다.
상속을 끊는다는 표현보다는 단일 상속을 잘 지킨다는 표현이 더 맞을 것 같다.
상속을 알고있다면 조금만 생각을 해보면 쉽게 알 수 있는 부분이다.
기본적으로 자바에서 상속은 부모클래스의 메서드나 필드를 받아서 사용할 수 있다. 그리고 부모클래스 또한 상속을
받고 있다면 자식클래스 또한 그 부모클래스가 상속을 통해 받아온 메서드나 필드를 이용할 수 있는 것이 자바의 상속이다.
그렇다면 부모클래스가 상속을 안받는 경우라면 Object클래스를 당연히 상속을 받고 있다고 생각을 해볼 수 있다.
그렇다. 부모클래스의 부모클래스의 부모클래스로 올라다가 보면 어차피 Object클래스를 상속을 받는 구조일테니
모든 클래스는 Object상속을 필수적으로 받을 수 밖에 없다는 얘기다.
'Java' 카테고리의 다른 글
[Java] 변수 초기화에 대한 이해 (0) | 2024.11.05 |
---|---|
[Java] Annotation 이란? ( 애노테이션 ) (0) | 2024.11.05 |
[Java] JVM 동작 원리 (0) | 2024.10.23 |
[Java] 클래스와 객체의 정의 (9) | 2024.10.05 |
[Java] 예외 ( Exception ) (2) | 2024.04.26 |