[Java] Java의 정석 정리 (상속)

송병훈·2022년 9월 24일
0

자바의 정석

목록 보기
5/15
post-thumbnail

상속 (Inheritance)

상속이라는 말은 부모의 재산을 자식이 물려받을 때 사용하죠.
그럼 Java에서는 상속을 어떤 의미로 사용할까요?

클래스에서 사용하는 말입니다.
부모 클래스와 자식 클래스가 있고,
부모 클래스의 멤버(변수, 메소드)들을
자식 클래스가 물려받아 사용하는 겁니다.

그럼 코드로는 상속을 어떻게 표현 할까요?
클래스명 뒤에 extends (= 확장하다) 키워드를 붙여 표현합니다.
왜냐하면 '부모로부터 상속받은 멤버들' + '자신만의 멤버들' 이므로
멤버가 확장되기 때문입니다.

이렇게 되면 코드의 중복이 줄어들겠죠?
똑같은 멤버변수나 메소드를 정의할 필요가 없어지잖아요.
그렇기에 사용하는 겁니다.

class Child extends Parent {	// Parent 클래스로부터 상속받음
	int x = 200;
	void method() {
		System.out.println("Child Method");
	}
}

그렇다면 상속의 특징을 정리해볼까요?

  1. 자식은 부모의 모든 멤버를 상속받는다. (생성자, 초기화 블럭 제외)
  2. 자식의 멤버 개수는 부모와 같거나 많다.
  3. 공통부분은 부모에서 관리하고, 개별부분은 자식에서 관리한다.
  4. 부모 클래스의 변경은 자식 클래스에 영향을 미치지만,
    자식 클래스의 변경은 부모 클래스에 영향을 줄 수 없다.
  5. Java는 단일상속만을 허용한다.

전에 배웠던 클래스와 객체, 메소드에 대한 내용 뿐만 아니라,
상속도 제대로 이해하고 계셔야 합니다.
이후에 상속으로부터 수많은 개념들이 파생되기 때문이죠.

  • 오버라이딩
  • super
  • 다형성
  • 추상클래스
  • 인터페이스

지금 배우고 있는 이 내용들은 기본 중의 기본입니다..? ㅎㅎ


포함 (Composite)

근데, 상속이 아니더라도
중복을 제거하여 변수를 사용하는 방법이 있습니다.
클래스 안에 다른 클래스의 객체를 생성하는 것이죠.
이것을 "포함" 관계라 부릅니다.

class Point {
	int x;
    int y;
    
    Point() {}
    Point(int x, int y) {
    	this.x = x;
        this.y = y;
    }
}

class Circle1 {		// 상속도 포함도 아님. 그래서 코드가 중복됨
	int x;
    int y;
    int r;
    
    Circle1() {}
    Circle1(int x, int y, int r) {
    	this.x = x;
        this.y = y;
        this.r = r;
    }
}

class Circle2 extends Point {	// 상속. x,y를 물려받음
    int r;
    
    Circle2() {}
    Circle2(int x, int y, int r) {
    	super(x, y);
        this.r = r;
    }
}

class Circle3 {
	Point point = new Point();	// 이것이 포함관계
    int r;
    
    Circle3() {}
    Circle3(int x, int y, int r) {
    	point.x = x;
        point.y = y;
        this.r = r;
    }
}
Circle1 c1 = new Circle1(1,2,3);
Circle2 c2_1 = new Circle2(4,5,6);
Circle2 c2_2 = new Circle2(7,8,9);
Circle3 c3 = new Circle3(10,11,12);

이 상황을 그림으로 쉽게 보자구요.

  • c1은 x, y, r 를 자기 클래스 안에서 생성했습니다. Point 클래스와 x,y가 중복이 있죠.
  • c2_1과 c2_2는 x, y 를 Point 클래스로부터 상속받고, r은 자식클래스에서 생성했습니다.
  • c3은 x, y 를 Point 클래스를 포함시켜 생성하고, r을 생성했습니다.

--

  • 네 개의 인스턴스 모두
    저장공간은 x, y, z로 동일합니다.
    (c3의 point는 주소를 가리키는 참조변수공간)

  • 그리고 c2_1과 c2_2는 x, y 를 Point 클래스로부터 상속받았지만
    서로 x, y 값이 공유되지는 않습니다.
    Point 클래스에서 x, y를 static을 이용해 클래스 변수로 선언하지는 않았으니까요.
    상속받은 변수들이라도 각각의 인스턴스에 생성되는 겁니다.

  • 상속은 "상속받아 사용한다"는 느낌이 강하게 들고,

  • 포함관계를 보면 이런 느낌이 듭니다.

    "상속받지 않고, 나만의 공간을 만들어서 사용할래!!"

개발자들은 이런 독립된 느낌을 선호하는가 봅니다.
남궁성님께서 인스턴스를 만들 때
포함관계가 90%, 상속이 10% 라고 하시네요.
상속은 꼭 필요할 때만 사용한다고 해요.

그럼 어떨 때 상속을 사용하고, 언제 포함관계를 사용하는 걸까요?
문장을 만들어보면 알 수 있어요.

  • 상속관계: '-은 ~이다' (- is a ~)
  • 포함관계: '-은 ~를 가지고 있다' (- has a ~)

방금 만들었던 원과 점을 예시로 들어볼까요?

  • ex) 원은 점이다.
  • ex) 원은 점을 가지고 있다.

포함관계가 어울리네요

그래서 여기서는 포함관계로 코드를 구현하는 게 낫습니다. 이렇게요.

class Circle3 {
	Point point = new Point();	// 이것이 포함관계
    int r;
    
    Circle3() {}
    Circle3(int x, int y, int r) {
    	point.x = x;
        point.y = y;
        this.r = r;
    }

Object 클래스

추가로 Object 클래스라고 있어요.
이 클래스는 모든 클래스의 최고 조상이에요.
트리구조의 root node와 같죠.

그래서 extends 키워드가 붙지 않은 클래스들은
컴파일러가 컴파일 할 때 extends Object 를 붙여주어
저절로 Object 클래스를 상속받게 됩니다.

이 클래스에는 11개의 메소드가 있습니다.
그래서 이 11개의 메소드를 기본으로 상속받습니다.
그 중 몇 개만 예시로 들자면

  • toString()
  • equals(Object obj)

와 같은 메소드들이죠.


내용이 길었네요!
오늘도 한 걸음 더 나아갔습니다!!
화이팅!!

profile
성실하고 꼼꼼하게

0개의 댓글