회고록 블로그

[공부 필기] Java 기본 공부하기 (13) 본문

2. 프로그래밍 언어 공부/Java

[공부 필기] Java 기본 공부하기 (13)

김간장 2021. 11. 8. 22:11

공부 중인 강의 : 윤성우 선생님, 윤성우의 열혈 Java 프로그래밍 강의, https://cafe.naver.com/cstudyjava

 

윤성우의 프로그래밍 스터디그룹 [C/... : 네이버 카페

윤성우의 스터디 공간입니다. C와 JAVA를 공부하시는 분들은 모두 들어오세요. ^^

cafe.naver.com


1. 상속 (드디어 상속이다)

- 상속은 코드 재활용을 위한 문법? No

- 상속은 재활용(코드 재사용)을 목적으로 정의된 문법이 아니라고 함 (물론, 단순히 보기에는 재활용처럼 보이기는 함)

- 상속은 연관되어 있는 일련의 클래스들에 대해서 공통적인 규약을 정의하는 것임

   → 상속을 쓰더라도 이 의미가 없으면 상속이 아님

   → 개인적으로 생각하기에 위의 문장은 이런 의미와 유사한 것 같음

         * 서로 다른 지역에 사는 중학생들이 여러명 있다고 가정 (= 각자 다른 곳에 사는 클래스들)

         * 이들은 모두 "중학교"를 다닌다는 공통점이 있음 (= 연관이 있음)

         * 이들에게 모두 "교복을 가지고 있어야 한다"는 약속을 적용하려고 함 (= 공통적인 규약을 정의)

   → 이처럼 A, B, C 라는 클래스는 어떠한 연관이 있고

         그 클래스들에게 공통적인 규약을 정의 해야할 때 "상속"을 사용하는게 아닐까 싶음 (추측)

 

 

# '상속이 재활용이 아닌' 이유에 대해 이해해보기

- 보통 "공학"(기계공학, 건축공학 등)에는 "표준"이라는 것이 존재하고

   이 "표준을 근거로 해서 재활용"이라는 것이 어느정도 가능함

   → 즉, 표준화 덕분에 재활용이 가능해졌다는 의미

- 하지만 SW는 아직 재활용이 어려움

   → 여기에서 말하는 "재활용"은 (코드 레벨에서) 하나도 수정하지 않고 그대로 가져와 사용할 수 있는 것을 말함

   → SW 분야는 코드를 하나도 수정하지 않고 그대로 가져와 사용하는 것이 아직 어려움..

- 때문에 SW의 세계는 다른 프로그램의 코드를 수정 없이 그대로 가져와 사용하는 것보다 다시 코딩하는데 더 안정적이라고 함

   → 아직까지는 그렇다고 함..

   → "컴포넌트"라는 개념을 도입해서 시스템을 구축하면 'SW도 재활용이 가능할 것이다'라는 연구가 나오고 있다고는 함

- 그렇기 때문에 상속이 "재활용"을 위한 문법이라고 하기 어렵다고 함..

- 참고 자료 : 

https://jaehun2841.github.io/2020/07/12/object-chapter12/#%EC%83%81%EC%86%8D%EC%9D%98-%EC%98%A4%ED%95%B4%EC%99%80-%EC%A7%84%EC%8B%A4

 

Objects Study - Chapter12. 다형성 | Carrey`s 기술블로그

상속의 오해와 진실 코드 재사용을 목적으로 상속을 사용하면 변경하기 어렵고 유연하지 못한 설계에 이를 확률이 높아진다. 상속의 목적은 코드 재사용이 아니다. 상속은 타입 계층을 구조화

jaehun2841.github.io

 

# SW - 코드 재사용에 대한 조사

- 이 강의는 2017년 강의라 트렌드 반영이 안된 듯 하여 최근의 코드 재사용에 대한 추세를 찾아봄

- Architectural patterns, Design patterns, Component-based development, Application frameworks, Legacy system wrapping 등의 방법이 있다고 하는데.. 너무 어려워서 아직 이해를 못하겠음..

- 출처 : 

- https://levelup.gitconnected.com/software-engineering-best-practices-for-reusing-in-software-development-6006f9d8d364

 

Software Engineering: Best Practices for Reusing in Software Development

Don’t Reinvent The Wheel

levelup.gitconnected.com

 

 

2. 상속 기본 특성

- 코드 레벨에서의 '상속'

   → extends 키워드를 이용함

   → 아래 코드는 "Student 클래스는 Person 클래스를 상속한다"는 의미

   → Student 인스턴스 내부에서 Person 인스턴스의 멤버 변수/메소드에 접근할 수 있음

class Person {
	...
}

class Student extends Person {
	...
}

- 코드 레벨에서의 '상속' 효과 알아보기

   → Student 인스턴스를 생성하면,

         해당 인스턴스에는 Person의 인스턴스 멤버(변수, 메소드들 모두)와 Student 인스턴스 멤버(변수, 메소드들 모두)가

         하나로 묶여서 형성됨 (하나의 인스턴스 안에 함께 존재하는 것)

   → 그림으로 표현하면... 이런 모습? (그림1 참고)

[그림1] 실제로는 인스턴스 안에 Student와 Person의 멤버 변수/메소드들이 "경계 없이" 존재함

- 하지만, 부모 클래스가 private이면 조금 다름

   → 하나의 인스턴스 안에 변수/메소드들이 함께 존재하는 것은 똑같음

   → 다만, Student 인스턴스가 Person 인스턴스 멤버 변수/메소드에 접근은 하지 못함

printYourName 메소드가 public 일 때 (참고로 좋은 코드는 아님..)
printYourName 메소드가 private 일 때 (Student 클래스에서 해당 메소드에 접근하지 못함)

- 보통 Person 클래스를 "상위 클래스"(또는 기초 클래스, 부모 클래스) 라고 함

   그리고 Student 클래스를 "하위 클래스"(또는 유도 클래스, 자식 클래스) 라고 부름

   → Person은 "상위 클래스"라고 하고 Student는 "자식 클래스"라고 하는 등 크로스해서 섞어서 용어를 사용하지는 않음

   → "상위 클래스"라는 용어의 짝은 "하위 클래스"이고

         "부모 클래스"라는 용어의 짝은 "자식 클래스"임

- 상속을 UML로 표현하면 이렇게 표현 가능함

   → 기회가 되면 "UML"을 공부하는 것도 괜찮음

 

3. 상속 & 생성자

- 아래와 같은 상황이 있다고 가정

   → Student 생성자에서 인자로 이름과 번호를 전달받고 부모 클래스의 멤버 변수인 "name"을 초기화 해야함

public class Person {
	private String name;
	
	Person(String name) {
		this.name = name;
	}
	public void printYourName() {
		System.out.println(name);
	}
}
public class Student extends Person {
	private int number;
    
    // Student 인스턴스를 생성해서 생성자에서 부모 클래스의 name 변수를 초기화하고 싶음
    ...

 

- 단순하게 생각하면?

   → 아래와 같이 코드를 작성

public class Student extends Person {
	private int number;
	
	Student(String name, int num) {
		this.name = name;	// 목적: 부모 클래스 Person의 멤버변수 name을 초기화 하고 싶음
		number = num;
	}	
	
}

   → 하지만 이렇게 실행하면 "에러가 발생함" (그림2 참고)

         * 사실 이 방법을 적용할 때, 운 좋게(?) 오류가 발생하지 않았더라도 좋은 방법은 아님

         * 왜냐하면, 

            클래스(여기에서는 Person)의 멤버변수는 그 클래스(Person)의 생성자 안에서 초기화 시켜주는 것이 가장 좋기 때문

[그림2] 에러발생

 

- 이런 오류가 발생하는 이유는 이러함

   → 우선 다시 복습해야할 내용 :

         * 디폴트 생성자(기본 생성자)는 "생성자가 전혀 정의되어 있지 않을 때 컴파일러가 자동으로 생성해서 넣어주는 것"

         * 개발자가 하나라도 생성자를 정의해준게 있다면 컴파일러는 디폴트 생성자를 만들지 않음 (그림3 참고)

         * 즉, 생성자가 오버로딩 되어 있으면 컴파일러는 디폴트 생성자를 만들지 않음

그림3. 디폴트 생성자를 정의하지 않았다는 에러 발생

   → Java 문법에는 규칙이 있음

   → 자식 클래스의 생성자는 "반드시" 부모 클래스의 생성자를 먼저 자동으로 호출함

   → 만약 개발자가 명시적으로 부모 클래스의 생성자를 호출하지 않았다면?

         컴파일러가 자동으로 생성자 안의 가장 첫 줄에 부모 생성자를 호출하는 코드를 넣어줌

   → 이제 위의 오류가 발생한 상황으로 가보자

         * 컴파일러는 자식 클래스의 생성자 안에 자동으로 부모 클래스의 생성자를 호출하는 코드를 넣어줌

         * 이때 부모 클래스의 기본 생성자를 호출하는 코드를 넣어주게됨

         * 문제는 부모 클래스에는 "명시적으로" 기본 생성자를 정의해두지 않았음

         * 이 때문에 에러가 발생함

에러 발생 원인 정리

 

참고한 자료 : https://blog.daum.net/genesis_8/9

 

상속, 조상클래스의 생성자.

this() 와 마찬가지로, super() 또한 '생성자' 단, this()는 '같은 클래스의 다른 생성자' 를 호출할때 쓰이며 spuer()는 '조상 클래스의 생성자'를 호출하는데 사용된다. 자손 클래스의 객체(Instatnce)를 생

blog.daum.net

 

- 따라서 이 오류를 해결하는 방법은 두가지임

   → 첫번째. 부모 클래스 Person에게 기본 생성자를 명시적으로 추가해줌

class Person {
	public String name;
	
	Person() {
	}
    ...

   → 두번째. 자식 클래스 Student에서 부모 클래스의 생성자를 호출할 때, 인자를 전달하면서 호출함

         * super 키워드는 상위 클래스의 생성자를 호출하는 기능을 함

         * super 키워드는 하위 클래스의 생성자에서만 사용할 수 있음

public class Student extends Person {
	private int number;
	
	Student(String name, int num) {
		super(name); // 부모 생성자를 호출한다. 이때 인자는 name을 전달한다. 는 의미
		number = num;
	}	
	...

    → 개인적으로 생각하기에,

         "클래스 멤버변수는 해당 클래스의 생성자에서 초기화 해주는 것이 가장 이상적"이라고 강의에서 말했기 때문에

         첫번째 방법보다는 두번째 방법이 좀 더 괜찮은 방법이지 않을까싶음

 

4. 상속 - 기타 특징

- 상속은 "단일 상속"만 지원함

   → Student 클래스가 Person 클래스와 Woman 클래스를 동시에 상속하는 것 = 안됨

   → JAVA는 다중 상속을 지원하지 않는다고 함

   → 한 클래스가 상속할 수 있는 최대 클래스의 수는 한 개임

- 하지만, 이런 경우는 가능함

   → Person 클래스와 Woman 클래스와 Student 클래스가 있음

   → 부모 클래스 : Person

         자식 클래스 : Woman

   → 부모 클래스 : Woman

         자식 클래스 : Student

    → Woman 클래스는 Person의 "자식 클래스"이자 Student의 "부모 클래스"임

    → 이처럼 A라는 클래스의 부모 클래스이자 B라는 클래스의 자식 클래스가 되는 관계를 만들수는 있음

         (클래스들을 상속 관계로 만들다보면 체인처럼 엄청 길어질수도..)

Comments