상속
현실에서 상속은 부모가 자식에게 무언가를 물려주는 행위를 말한다. java에서 상속도 다른 것이 거의 없다. 부모 클래스가 자식클래스에게 물려줄 수 있다.
상속의 특징
•
단일 상속
•
최상위 클래스는 Object 클래스이다.
•
부모메서드 재정의
•
private 키워드가 붙은 메서드 상속 불가
•
final 클래스, 메서드 상속 불가
궁금증 final, private 키워드가 붙은 메서드는 둘다 상속 못하는데 똑같은 거야?
처음 final과 private의 상속 관점에서는 거의 동일하다고 생각했다. 하지만 조금 생각해보니 final의 의미는 최종적 그러니 더 이상 수정을 할 필요가 없다. 하지만 private는 개인적 즉, 다른 외부 패키지에서 조차도 메서드를 사용할 수 없다는 것이다. 즉, final은 완성된 메서드 또는 클래스를 수정하지 못하게 하는 목적을 가지고 있고, private의 경우 외부에서 메서드를 사용하지 못하게 하는 목적을 가지고 있다.
”private 은 접근 수정자이고, final 은 추가 제약 조건을 부여하는 수정자입니다.”
상속 코드
위의 상속을 이용한 코드에서도 볼 수 있듯이 Person이라는 class는 말하는 방법을 알고 가지고 있다. 그것을 한국인.class와 일본인.class에 상속하여 재정의 하였다. 덕분에 같은 Person Type에 한국인 또는 일본인 인스턴스를 할당해도 재 정의된 기능을 온전히 사용할 수 있다.
왜 상속을 쓰는 것일까?
이미 잘 만들어진 클래스를 재 사용해서 새로운 클래스를 만들기 쉽기 때문이다. 이렇게 말하면 좀 어렵게 느껴진다. 그래서 필자가 느끼기에는 상속을 사용하는 것은 우리가 현실에서도 부모의 재산을 물려받으면 사회생활에 출발점이 다르듯 물려주는 행위 덕분에 할 수 있는 것은 많아지기 때문이다.
super?
모든 객체는 클래스의 생성자를 호출해야만 생성된다. 부모 객체도 예외는 아니다. 그렇다면 부모 객체를 생성하기 위해 부모 생성자를 어디서 호출 할 것인가? 비밀이 숨겨져 있다. 자식 객체 즉, 상속관계가 이루진 상태에서는 자동적으로 부모 생성자를 만들어 낸다.
public class Main {
public static void main(String[] args) {
Person 한국인 = new 한국인();
한국인.talk();
}
}
CONSOLE RESULT
부모 생성자???
한국말~
class Person {
public Person() {
System.out.println("부모 생성자???");
}
public void talk(){
System.out.println("사람은 각자의 언어로 대화할 수 있습니다.");
};
}
class 한국인 extends Person{
@Override
public void talk() {
System.out.println("한국말~");
}
}
Java
복사
이렇게 되는 비밀은 컴파일러가 개발자가 직접 작성하지 않더라도 부모클래스의 디폴트 생성자를 호출해주기 때문이다. 좀 찾아보니 별다른 이유에 대해서 기재한 정보를 찾을 수 없었다. 그래서 내가 생각할 때 추측으로는 명시적 호출 super(…)를 사용할 때 제일 첫줄에 적어야하는 것과 관련성이 있는 것 같다. 이유는 필드 등도 상속 받을 수 있는데 부모 필드가 초기화 되지 않은 상태에서 자식에서 사용하려는 경우를 방지하려는 목적도 존재하는 것 같다.
메서드 오버라이딩
부모 클래스의 모든 메서드가 자식 클래스에게 맞게 설계되어 있다면 가장 이상적인 상속이지만, 어떤 메서드는 자식 클래스가 사용하기에 적합하지 않을 수 있다. 이 경우 상속된 이부 메서드는 자식 클래스에서 수정해서 사용한다(앞서 살펴본 Person의 talk()를 한국인과 일본인에 맞게 수정한 부분).
메서드 오버라이딩의 특징
•
부모의 메서드와 동일한 시그니처를 가져야한다.
•
접근 제한을 더 강하게 오버라이딩 할 수 없다.
•
새로운 예외를 throws 할 수 없다.
예전에 메서드오버라이딩으로 재미있는 문제를 하나 푼적이 있었다. 이 문제는 단일 상속에서는 문제가 될 것이 없지만 interface의 다중 상속을 할 때 일어날 수 있는 문제이다. 잘 풀어보장. 예전에 내가 interface의 default메서드를 공부하며 몇가지 상황에 대한 예시를 공부하고 필기한 내용이다. (다이아몬드 문제)
추상 클래스?
객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상클래스라고 이야기한다. 추상 클래스와 실체 클래스는 상속의 관계를 가지고 있다. 추상 클래스가 부모이고 실체 클래스가 자식으로 구현되어 실체 클래스는 추상 클래스의 모든 특성을 물려 받고, 추가적인 특성을 가질 수 있다.
추상 클래스의 특징
•
인스턴스를 만들 수 없다.
•
추상 클래스의 메서드는 반드시 구현해야 한다.
•
미완성 클래스이다.
예전에 내가 토비의 스프링 공부를 하며 다중 상속의 지원을 제외한다면 동일한 기능을 제공하는 인터페이스와 추상클래스가 무슨 차이가 있는지 공부한 적이 있다. 그걸 보면 좋을 것 같다.
final?
“최종”이라는 의미를 가지는 final 키워드의 경우에는 더 이상 수정할 수 없는 상태를 만들때 사용하는 키워드이다.
왜 final?
의문이 드는 주제이지 않은가? 왜 final이라는 키워드가 우리의 코딩 속에 중요한 주제인 것인가? final이 중요하다고 생각하는 이유는 “더 이상의 수정 불가”에 초점을 맞추면 좋을 것 같다. 이를 통해서 우리가 얻을 수 있는 이점은 코딩할 때 저 값이 또는 저 방법은 더 항상 동일함을 보장한다는 것이다. 가끔 그런 경우가 있을 수 있다. 코딩하다 보면 우연히 값을 수정해서 이상한 값이 나오는 경우가 말이다. 하지만 final이라는 키워드 덕분에 동일함을 보장할 수 있고 좀 더 안전한 코딩을 할 수 있다.
final 키워드가 붙으면?
•
변수 → 한번 초기화에 동일한 값 유지
•
파라미터 → final로 선언된 파라미터는 그 함수 어디에서도 파라미터 값을 변경하지 못함
•
메서드 → Override, hide 될 수 없음
•
클래스 → subClass를 가질 수 없음. 즉, 상속 불가
가끔 보이는 파라미터의 final?
가끔 보면 이렇게 final이 파라미터에 인자 값에 붙은 경우가 있다. 이 경우는 잘 사용하지 않지만 인자 값을 변경하지 못하게 할 때 사용하는 경우이다.
나와 비슷한 생각 왜 자바에서는 final멤버 변수는 관례적으로 static을 붙일까?
처음에 생각했던 것은 스프링에서 만약 빈을 만들어 사용하는데 상수를 만드는데 있어서 꼭 static final로 만들어야 할까? 이런 생각을 했다. 어차피 스프링에서는 빈이 한번만 만들어지고 그 빈을 재사용하는 형태라면 상수를 만들어야할 일이 있을때 꼭 static을 붙여야할까? 라는 생각을 했다. 이 부분에서 궁금증이 생겨서 찾아봤는데, 내가 본질적으로 궁금했던 내용은 없었다. 그런데 시간이 조금 지나고 생각해보니 상수는 스프링에서 enum으로 따로 관리하는 것이 좋아 보였다.
Object 클래스
클래스를 선언할때 extends 키워드로 다른 클래스를 상속하지 않으면 암시적으로 Object 클래스를 상속한다. 따라서 자바의 모든 클래스는 Object 클래스의 자식이거나 자손이다. 흔히 사용할 경우 equals(), hashCode(), toString()을 override해서 사용한다.