본문 바로가기
Language/Java

JAVA입문-6 (상속)

by 드럼치는 코린이 2021. 2. 27.
728x90
반응형


[상속]

Java에서 상속이란 부모 클래스가 가진 것을 자식 클래스가 물려받는 것을 말한다.
즉, 부모 클래스가 가진 것을 자식 클래스가 사용할 수 있다.

is a , kind of 관계

"버스는 차입니다" 여기서 버스는 자식 클래스, 차는 부모 클래스.

이클립스에서 새로운 클래스를 만들 때 Superclass를 통해 원하는 부모 클래스를 설정해 상속받도록 한다.

또는 public class 클래스명 extends 부모 클래스명을 통해 직접 설정해도 된다.


ex. 상속받아 사용하는 방법(1)

1. Car를 부모 클래스로 상속받는 자식 클래스 Bus를 만든다.

2. Car클래스에 run 메서드를 만든다.

3. BusExam클래스에 Bus클래스를 하나 생성한 후 bus에 run메서드를 호출하면 Bus클래스는 Car부모 클래스를 상속받기 때문에 Car클래스에 있는 run메서드를 불러와 "달리다"라는 값을 출력하게 된다.


ex. 상속받아 사용하는 방법(2)

1. Bus클래스에 ppangppang 메서드를 만들면, Bus클래스는 ppangppang메서드와 Car를 상속받아 run메서드까지 총 2가지의 메서드를 사용할 수 있게 된다.

2. bus에 ppangppang메서드를 호출하면 "빵빵"이란 값이 출력된다.

방법(2)처럼 Bus클래스는 부모 클래스에 있는 메서드 외에도 추가로 메서드를 선언하는 것을 확장하였다고 한다.


ex. 반대로 BusExam클래스에 부모 클래스인 Car클래스를 만들어 메서드를 사용해 본다면?

Car클래스는 run메서드를 가지고 있기 때문에 run메서드를 호출할 수 있지만, ppangppang메서드는 상속받지 않고 있기 때문에(부모 클래스는 자식 클래스의 기능을 사용할 수 없다) ppangppang메서드를 호출할 수 없어 error를 발생시킨다.


[접근 제한자]

클래스는 필드와 메서드를 가진다.

필드와 메서드를 가진다고 아무거나 가지면 안 된다. 어떠한 주제한 관련된 필드나 메서드를 가져야 한다.

이것을 '캡슐화'라 한다.

*캡슐화 : 관련된 내용을 모아서 가지고 있는 것

모아서 가지고 있는 필드와 메서드 중에는 외부에 노출시키지 않고 감추고 싶은 필드와 메서드가 있을 수 있다.

그래서 접근제한자중에는 public, protected, private가 있고 아무것도 지정하지 않은 default 접근 지정자가 존재한다.

*public : 모든 접근을 허용, 가장 넓은 의미 (전체 공개와 같은 의미)

*protected : 같은 패키지인 경우 접근 허용, 다른 패키지라도 상속을 받은 경우 접근을 허용

*private : 자기 자신만 접근 가능

*default : 아무것도 쓰지 않은 경우. 자기 자신과 같은 패키지 내에서 접근 허용

따라서 공개범위가 큰 순으로 보면... public > protected > default > private


ex. 접근 제한자를 갖는 클래스를 사용해보면

1. AccessObj클래스에 3가지의 접근 제한자와 1가지의 접근 지정자를 선언

2. AccessObjExam클래스에 AccessObj 객체를 만들고 3가지의 접근 제한자와 1가지의 접근 지정자를 출력하면 private의 접근 제한자는 error를 발생시킨다. private접근 제한자는 자기 자신만 접근이 가능하기 때문.


ex. AccessObjExam클래스를 다른 패키지로 옮겨보면?

public접근 제한자 외에는 error발생.


ex. 만약 AccessObjExam클래스가 AccessObj클래스를 상속받는다면?

다른 패키지이지만 AccessObj클래스를 상속받고 있기 때문에 error를 발생시키지 않는다.


따라서 접근 제한자는 필드뿐만 아니라 메서드와 클래스 앞에도 붙여 사용할 수 있고,

접근 제한자를 통해 해당 클래스나 해당 필드, 해당 메서드에 어디까지 접근해 사용하게 할지 정의할 수 있다.


[추상 클래스]

'새'라는 단어를 생각하면 사람에 따라 독수리, 타조, 앵무새 등을 생각한다.(구체적 클래스를 만들기 모호하다. 그래서 추상 클래스를 만든다)

그런데 '비둘기'라는 단어를 생각을 하면 좀 더 구체적이기 때문에 비슷한 형상을 떠올릴 수 있게 된다.(구체적 클래스 만들 수 있음)

그래서 추상 클래스는 부모 클래스로서의 역할은 수행할 수 있지만 객체가 될 순 없다.

구체적인 클래스만 객체를 가질 수 있다.

추상 클래스를 만들면 나중에 관련된 클래스를 사용할 때 부모 클래스로서의 역할로 사용할 수 있다.


ex. Bird라는 클래스를 만들어 보자.

sing메서드를 만드는데 Bird가 어떤 소리로 우는지 알 수 없기 때문에 추상 메서드를 만들어 준다.

public abstract 리턴 타입 메서드명();

그리고 메서드가 하나라도 추상 메서드인 경우 해당 클래스는 추상 클래스가 될 수밖에 없다.

public abstract class 클래스명{...}

+ 해당 클래스가 추상 클래스라도 일반 메서드 구현 가능.


ex. Bird클래스를 상속받는 Duck클래스를 만든 후 DuckExam클래스를 통해 Bird클래스와 Duck클래스의 메서드를 호출해보면

DuckExam클래스에 Duck객체를 만들고 Duck클래스에 있는 sing메서드를 호출하면 "꽥꽥"이 출력된다.

또 Duck클래스는 Bird라는 추상 클래스를 상속받고 있기 때문에 Bird클래스에 있는 fly메서드도 호출할 수 있다.


ex. 추상 클래스는 부모 클래스로서 역할은 가능하지만 추상 클래스 자체를 이용해 객체를 만드는 등 사용할 수는 없다.


[Super와 부모 생성자]

ex. 

1. Car클래스를 만들고 Car 기본 생성자를 하나 만든다.

2. Car를 상속받는 Truck클래스를 만들고 Truck 기본 생성자를 하나 만든다.

3. TruckExam클래스를 만들고 Truck과 Car의 객체를 각각 만들어 기본 생성자를 실행시킨다.

여기서 Truck 기본 생성자를 실행시켰을 때 Truck의 기본 생성자보다 Car의 기본 생성자가 먼저 출력이 된 이유는 부모 클래스가 없이 자식 클래스가 있을 수 없는 것처럼 부모 클래스의 생성자가 먼저 실행되고 다음으로 자식 클래스의 생성자가 실행되기 때문이다.

따라서 생성자가 하는 일은 객체를 초기화하는 일인데, 생성자가 호출될 때 자동으로 부모 생성자가 호출되면서 부모 객체를 초기화하고 그 후에 자식 생성자가 호출이 되는 것이다.


*super : 부모 객체를 나타내는 키워드

ex. 생성자를 만들었을 때 컴파일러는 자동적으로 super키워드를 생성시킨다.

1. super(); 부분이 컴파일러가 자동적으로 생성하는 부분이다.

2. 따라서 super();를 입력해도 결과는 같으며, super();를 통해 부모 객체가 먼저 출력되고 자식 객체가 출력되는 것이다.

여기서 기억해야 할 부분은 나를 가리키는 키워드 this, 부모를 가리키는 키워드는 super!!


ex. super는 컴파일러가 자동적으로 생성하는데 super를 알아야 하는 이유?

1. Car클래스에 기본 생성자를 없애고 Car에 name을 받는 생성자를 추가

2. Truck클래스의 오류 발생. 그 이유는 Truck 생성되기 위해서는 반드시 부모인 Car생성자가 먼저 생성돼야 하는데, 부모 생성자를 직접 호출하지 않으면 컴파일러는 기본 생성자만 호출하게 된다. 그런데 부모 클래스는 기본 생성자를 가지고 있지 않기 때문에 Truck클래스에 error를 발생시킨다.

3. 따라서 직접 super를 통해 부모 생성자를 직접 호출해줘야 한다.

4. 실행 결과


[오버 라이딩(Overriding)] : 올라타다

오버 라이딩이란 메서드를 재정의 하는 것.

부모가 물려준 기능이 필요한데 그대로 사용하기엔 좋지 않을 경우 부모가 물려준 메서드를 자식이 다시 정의해서 사용하는 것을 오버 라이딩이라고 한다.

즉, 오버 라이딩은 부모 클래스의 메서드를 수정하고 싶을 때 아주 유용하게 쓰인다.


ex. 부모 클래스의 메서드와 동일한 메서드를 자식 클래스에 재정의

1. Car클래스에 run메서드를 만든다.

2. Car를 상속하는 Bus클래스를 만든다.

3. BusExam클래스에 Bus객체를 하나 만들고 Car클래스의 run메서드를 호출하면 "Car의 run 메서드"가 출력된다.

4. Car를 상속받고 있던 비워져 있는 Bus클래스에 Car클래스와 동일한 run메서드를 만든다.

5. BusExam클래스를 다시 실행시켜보면 부모 클래스가 가진 메서드가 아예 실행되지 않고 자식 클래스의 run메서드가 실행되어 "Bus의 run 메서드"가 출력된다.

그 이유는 부모의 메서드와 동일한 메서드를 자식이 재정의해서 올라가 있는 형태이기 때문(오버 라이딩)

하지만 오버 라이딩을 해도 부모의 메서드가 아예 사라지는 것은 아니다.


ex. 메서드를 오버 라이딩하면서 자식 클래스에서 부모 클래스도 호출하고 싶을 때

Bus클래스(자식 클래스)에 super.메서드명을 통해 자식 클래스에서 부모 클래스의 메서드를 호출할 수 있다.


[클래스 형 변환]

상속은 is a 관계. 즉, "~~는 --다" 관계가 성립되어야 한다.

현실에서도 "버스를 차다"라고 얘기하듯 Java에서도 같다.


ex. 부모 타입으로 자식을 가리킬 수 있으나, 이 경우 부모가 가지고 있는 내용만 사용 가능.

1. Car클래스에 run메서드 생성

2. Car클래스를 상속받는 Bus클래스 생성 후 ppangppang메서드 생성.

3. Car클래스가 자식 클래스를 가리킬 수 있지만 부모 클래스가 가진 내용 외에는 호출할 수 없다.


ex. 생성된 객체 Bus는 실제 ppangppang메서드를 갖고 있지만 Bus는 부모 타입을 가리키고 있어 사용하지 못함. 하지만 있는 기능을 꼭 사용하고 싶을 때 형 변환을 이용한다.

앞서 형 변환 시 큰 그릇을 작은 그릇에 담는 것(강제적 형 변환)과 작은 그릇을 큰 그릇에 담는 것(묵시적 형 변환)을 배웠듯 객체는 항상 부모 클래스가 큰 그릇을 가지고 있다.

그래서 Car타입으로 Bus를 가리키는데 문제가 없었다.

1. 그러나 자식 클래스의 Bus가 부모 클래스 Car를 가리키면 컴파일 error가 발생

2. Bus타입이 Car를 담을 수 없기 때문에 Bus타입으로 강제 형 변환을 통해 c를 가리키게 하여 Bus클래스의 ppangppang메서드를 사용할 수 있게 된다.


이것이 가능한 이유는 Car가 참조하는 객체가 원래 Bus였기 때문이다.

ex. 만약 Car가 Car를 참조했다면 error를 발생

따라서 클래스들끼리의 형 변환 부모가 자식을 가리킬 수 있으나 부모가 가리키고 있었을 때는 부모가 알고 있는 내용만 접근이 가능하다. 그렇기 때문에 형 변환을 해야 각 객체들이 갖고 있는 내용들을 모두 사용할 수 있게 된다.

728x90
반응형

댓글