Java 객체지향 Study 4회차

Geun Bo Kim·2023년 4월 12일
0

스터디

목록 보기
6/6

상속의 기초적인 개념

목표

import와 상속관계, overriding, super 키워드 등 상속의 기초적인 개념에 대해 이해함

스파르타 자바 문법 총정리 3 - 10 ~ 3 - 14의 내용을 기반으로 학습함

일요일 스터디는 쉬는걸로 ;)

다들 즐거운 휴일 보내세요 🙂

재충전 하고 오세용

김근보 5

김은양 2

신동현 4

장기웅 3

고예진 1

사전 질문

  1. import는 내가 사용할 클래스의 경로를 입력합니다.

“자바스터디”라는 패키지에 “사전준비”라는 패키지가 있고 그 안에 “사전질문”이라는 클래스가 있을 때 “사전질문” 클래스를 이용하고 싶습니다. import를 이용해 알맞게 경로를 입력 해 주세요.

  • 고예진(답변)
    import javastudy.preliminary.preliminary;
           자바스터디 패키지. 사전준비 패키지. 사전질문 클래스;
    
    public class Answer {
    
    }
  • 김은서(출제의도) IDE를 이용하면 import 키워드를 자동으로 불러오기 때문에 간단하게나마 import로 경로 설정 해 주는 거 한 번 하면 좋을 것 같아서 문제를 냈다.
    import 키워드 뿐만 아니라 package 키워드도 있다.
    import 같은 경우에는 내가 쓰고 싶은 클래스의 경로를 설정해 주는 것이라면, package는 내 위치의 경로를 알려주는 개념이다.
  • 김은서(추가질문과 답) package 키워드를 이용해서 내 경로를 표시 해 준다면 어떻게 할 수 있을까?
    정답:
    package javastudy.preliminary;
           자바스터디 패키지. 사전준비 패키지;
    
    public class Answer {
    
    }
  1. 아래 코드를 실행했을 때 출력값을 예측해보세요.
class Animal {
    public void sound() {
        System.out.println("동물의 소리");
    }
}

class Dog extends Animal {
    public void sound() {
        System.out.println("멍멍");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound();

        Dog dog = new Dog();
        dog.sound();

        Animal animal2 = new Dog();
        animal2.sound();
    }
}
  • 김은양(답변)
    Animal animal = new Animal();
    animal.sound();
    Animal 클래스의 sound 메소드를 호출 “동물의 소리”
    Dog dog = new Dog();
    dog.sound();
    Dog 클래스의 sound 메소드 호출 “멍멍”
    Animal animal2 = new Dog();
    animal2.sound();
    Dog 클래스의 부모 클래스인 Animal 클래스의 sound 호출 “동물의 소리”
  • 고예진(정정)
    Animal animal2 = new Dog();
    animal2.sound();
    Dog 클래스의 부모 클래스인 Animal 클래스의 sound 호출 “동물의 소리”이지만 현재 overriding으로 인해 실제 출력은 “멍멍”
  • 김은서 (추가설명) 여기서 중요한 개념은 ‘다형성에서 오버라이딩이 어떻게 받아들여지는지’이다. 다형성을 이용해 다음과 같이 인스턴스를 생성하면
    Animal animal2 = new Dog();
    해당 객체는 부모 클래스의 메서드만을 사용할 수 있지만 부모 클래스의 메서드를 자식 클래스에서 오버라이딩 했다면 자식 클래스에서 오버라이딩 한 메서드를 호출할 수 있다. 간단히 다시 말 하자면 부모 클래스에만 있는 메서드와 오버라이딩 된 자식 클래스의 메서드만을 사용할 수 있게 되는 것이다. 때문에 animal2에서 sound를 호출하면 오버라이딩 된 sound가 호출된다.
  1. 다음의 질문에 답해보세요.

클래스 A는 변수 a와 메서드 methodA를 가지고 있습니다. 클래스 B는 클래스 A를 상속받고, 변수 b와 메서드 methodB를 가지고 있습니다. 클래스 C는 변수 c와 클래스 A를 포함하고 있습니다. 클래스 D는 클래스 B를 상속받고, 메서드 methodD를 가지고 있습니다. 그리고,
클래스 A의 인스턴스를 생성하고, 이를 변수 a에 저장합니다.
클래스 B의 인스턴스를 생성하고, 이를 변수 b에 저장합니다.
클래스 C의 인스턴스를 생성하고, 이를 변수 c에 저장합니다.
클래스 D의 인스턴스를 생성하고, 이를 변수 d에 저장합니다.

  1. 변수 b를 A 타입의 변수에 저장할 수 있나요?
  2. 변수 c를 B 타입의 변수에 저장할 수 있나요?
  3. 변수 d는 어떤 클래스를 상속받고, 어떤 클래스를 포함하고 있나요?
  4. 클래스 D 에서는 어떤 메서드를 사용할 수 있나요?
  5. 변수 a는 C 타입의 변수에 저장할 수 있나요? - 기웅님의 추가 질문
  • 장기웅(답변)
    1. 가능하다 : class B는 class A를 상속 받고 있기 때문에 변수 b를 a에 저장가능하다.

    2. 불가능하다 : class C는 class A 포함하지만, class B 와는 아무 관계가 아니기 때문에, 즉 상속을 받지 않기 때문에 불가능하다.

    3. class B를 상속받고 그 B는 class A를 상속 받고 있기 때문에 A와 B 클래스 모두를 상속 받는 것과 같다.

    4. class B 와 class A의 변수와 메소드 모두 사용가능

    5. 아니요.

      변수 a는 C타입으로 만든 객체에 참조변수 c를 저장할 수 있나?

      class c는 class A를 포함하고 있지만 상속은 아니 때문에 A는 c에 저장 될 수 없다.

      A 클래스의 인스턴스는 C클래스안에 존재할 뿐 상속은 아니라서 불가능하다.

  • 신동현(출제자의 추가설명) 각자 자기 자신에게 인스턴스를 생성하고, 각자 변수와 메소드를 가지고 있다. 이들의 상속관계와 다형성에 대한 내용이다.
    1. 클래스 B가 클래스 A를 상속 받았기 때문에 A의 모든 정보를 포함하고 있어 저장할 수 있다.
    2. 클래스 C가 클래스 A를 포함하지만, 상속은 아니다. 그래서 불가능하다.
    3. 클래스 D는 클래스 B에게 상속받고 있고, B는 A에게 상속 받고 있다. 간접적으로 A에게 상속 받고 있다라고 말할 수 있다. 따라서 클래스 D는 클래스 A와 B의 모든 멤버들을 포함하고 있다.
    4. 클래스 D는 클래스 A와 B 모두 상속 받아 그 안에 있는 메소드를 사용할 수 있다. 단, 클래스 C는 상속 관계가 아니므로 사용할 수 없다.
  • 김은서(추가설명) 이번 문제는 실제로 코드를 구현하기 보다는 관계도를 그려 객체 간의 관계에 관해 파악하는 게 중점인 문제라 생각한다. 해당 객체들의 관계를 관계도로 표현하면 다음과 같다.
  1. 자바에서 final 로 선언된 클래스는 더 이상 상속될 수 없는 이유는 무엇인가요?
  • 신동현(답변) 상속을 방지하기 위해 final 키워드가 사용되기 때문입니다. final로 선언된 클래스는 그 자체로 완전한 클래스이며, 그 클래스의 동작 방식을 변경하거나 수정하는 것이 불가능합니다. 따라서 서브 클래스에서 상속된 메소드나 멤버 변수들을 변경하거나 재정의 하는 것이 불가능 합니다.
    public final class Car {}
    ...
    public class Bus extends Car{} // 오류
  • 김근보(final의 추가 설명) 변수 : final이 적용된 변수의 값은 초기화 된 이후에는 변경할 수 없다 메서드 : final로 선언된 메서드는 하위 클래스에서 오버라이드 불가능하다. 클래스 : final로 선언된 클래스는 계승 대상이 될 수 없다 (하위 클래스 생성 불가능)
  • 김은서 (추가설명과 질문) final 키워드를 이용해 선언된 클래스는 그 자체로 완전한 클래스이다. 때문에 상속 받아서 기능을 변경하거나 확장하는 게 불가능하다. 그렇다면 final 키워드를 이용해 선언된 메서드는 오버라이딩 되는 것이 가능할까? 정답은 ‘불가능하다’이다. final은 말 그대로 변경 불가능이라고 생각하면 될 것 같다. 우리가 변수 앞에 final 키워드를 붙히면 상수라고 부르고 다시 값을 지정하지 못 하는 원리와 같다.
  • 장기웅(추가질문) 서브 클래스는 무엇인가?
  • 황석빈(추가질문에 대한 답) super클래스 : 상속을 하는 부모 클래스 서브클래스 : 부모 클래스를확장한(상속)한 클래스
  • 김은양(추가질문) static과 final을 같이 사용하는 코드를 많이 보았는데 이유가 있나?
  • 김은서(추가질문에 대한 답) static 키워드를 이용해 변수를 선언하면 클래스 변수가 된다. 그리고 해당 변수가 포함된 클래스를 인스턴스화 하면 인스턴스에서도 해당 변수에 접근하여 값을 변경하는 게 가능한데, 클래스 변수, 즉 전역 변수는 참조형이기 때문에 인스턴스에서 값을 변경하면 클래스 원본의 값까지 영향을 미친다. 그렇기 때문에 final 키워드를 이용해 변경 불가능의 형태로 만들어 이를 보완하는 역할을 할 수 있기 때문에 두 키워드를 함께 많이 사용한다고 생각한다.
  • 송우근(추가질문) final로 선언된 클래스는 더이상 상속 될 수 없지만, 상속할 수는 있나?
  • 신동현(답변) 네 가능합니다. 건님이 인텔리제이에서 실제 코드작성 하였을 때 가능했다.
  • 이 질문에 대한 대답은 완료가 되지 않은 것으로 압니다. 정확한 답변이 있는 분은 작성해주세요.
  1. 다음은 자식 클래스의 생성자에서 부모 클래스의 필드를 정의하는 코드입니다.
  • 부모 클래스
// 부모 클래스 Car 생성자
public Car(String model, String color, double price) {
    this.model = model;
    this.color = color;
    this.price = price;
}
  • 자식 클래스
// 자식 클래스 SportsCar 생성자
public SportsCar(String model, String color, double price, String engine) {
		super.model = model; //해당 코드
		super.color = color; //해당 코드
		super.price = price; //해당 코드
    this.engine = engine;
}

표시 해 놓은 세 줄의 코드를 한 줄로 줄이세요.

  • 김근보(답변)
    super(model, color, price);
    위 코드는 Car 클래스의 생성자를 호출하여 model, color, price 를 전달해 Car클래스의 필드를 초기화 하고 있다. super 키워드를 사용하면 부모 클래스의 생성자를 호출할 때, 인자로 전달하는 값을 바로 부모 클래스의 필드에 할당할 수 있다. (부모 클래스의 생성자가 부모 클래스의 필드를 초기화하는 역할을 수행하기 때문에) 정답 : super 를 사용하여 한 번에 모든 부모 클래스의 필드를 초기화할 수 있기 때문에 한줄로 쓸 수 있다. 사용이유 : super는 자식클래스에서 부모 클래스의 멤버(필드, 메서드, 생성자)를 참조하기 위해 사용한다. 주의사항 : 자식 클래스의 생성자에서 super 키워드를 사용하여 부모 클래스의 생성자를 호출하면, 부모 클래스의 생성자가 먼저 호출되고 이후에 자식 클래스의 생성자가 호출되므로 super 키워드를 사용하기 위해서는 반드시 첫번째 줄에 작성 해야한다. (왜냐하면 자식 클래스의 인스턴스 변수들이 초기화 되기 전에 부모클래스의 생성자가 호출 되어야하기 때문이다.) super() 는 부모의 생성자를 호출할 수 있다. 이 키워드를 이용하면 코드의 가독성을 높이고 중복 코드를 줄일 수 있다.
  • 김은양(추가질문) super를 이용하여 부모클래스의 필드에 접근하면, 변수가 달라지는가?
  • 김근보 달라지지 않는다고 알고 있다.

다음 클래스들이 있습니다. testMain 클래스를 디버깅하면 testB 와 testBB의 주솟값은 서로 다를까요? 다르다면 왜 다르고 같다면 왜 같은걸까요?

//test 패키지 안에 A 클래스
package test;

public class A {
    void methodA() {
        System.out.println("A");
    }
}

//동일 패키지에 B 클래스
package test;

public class B extends A{
    @Override
    void methodA() {
        System.out.println("B는 A 상속받았다");
    }
    void methodB() {
        System.out.println("이거는 B만의 메소드이다");
    }
}

//동일 패키지에 C 클래스
package test;

public class C extends A {
    @Override
    void methodA() {
        System.out.println("C도 A 상속받았다");
    }
    void methodC() {
        System.out.println("이거는 B만의 메소드이다");
    }
}

//main
package test;

public class testMain {
    public static void main(String[] args) {
        A testB = new B();
        A testC = new C();

        testB.methodA(); //B는 A 상속받았다
        testC.methodA(); //C도 A 상속받았다

        B testBB = (B)testB;
			
        System.out.println(testB);
        System.out.println(testBB); //testB와 testBB는 같은 주소값을 출력한다.

    }
}
  • 신동현(답변) 답: 결국 같은 주소값을 갖는다. 사실 다형성과 관련된 문제이다. A클래스 B클래스 - A 클래스 상속 받음 C클래스 - A 클래스 상속 받음 new 키워드 : heap에서 B만큼의 메모리를 할당 받겠다. 그런데 B는 A를 상속 받고 있다. ⇒ 초록색 박스 = B가 할당 받은 공간, 주황색 박스 = A가 할당 받은 공간 그런데 test B는 A타입으로 주황색 박스만큼만 바라볼 수 있다. 주황색 박스 : method A 초록색 박스 : method A 와 method B 오버라이딩에 의해 초록색 박스안에 method A를 정의하면 주황색 박스 methodA에 재정의라고 생각했다. 그러나 건님의 설명에 의해서는 아니라고 한다. 컴파일 단계에서 메소드를 호출하는 것이 아니라 런타임단계 즉, 프로그램을 실행할 때 상속 계층도 (맨 오른쪽 Dog클래스, Animal클래스, Object 클래스 이것을 상속계층도라고 한다. ) 메소드를 호출할 때 자손클래스부터 찾아가고, 찾게 되면 그 자리에서 호출 후 종료
    • 김은서

      메서드를 하위클래스부터 찾아가기 때문에, (2번 문제에서도 다뤘듯이) 다형성을 통해 인스턴스를 생성하였을 때 부모 클래스의 메서드를 자식 클래스에서 오버라이딩 한 경우 오버라이딩 된 메서드를 호출되는 형태가 나온다고 생각하면 될 것 같다. 

      즉, 하위클래스에서 오버라이딩이 되어 있으면 부모가 아닌 자식클래스에서 호출 된다.

      B testBB = (B) testB;

      testBB는 B타입이고, B타입은 A를 상속 받고 있고, testB는 A타입이다. 실제로 testBB는 초록색 박스 전체를 볼 수 있다. testBB라는 값을 testB로 초기화했으나, testB는 A부분밖에 보지 못하므로 B타입으로 형변환을 시켜주었다.

      따라서 testBB는 A와 B 모두를 호출 할 수 있다.

    • 김은서(질문)

      그렇다면 더 넓은 범위의 메서드를 가지게 된 testBB에서의 print A 와, testB에서의 print A는 같은가?

    • 신동현 (답변)

      같다. 메서드를 하위 클래스부터 찾아가는 형식은 변함 없으니 오버라이딩 된 메서드를 호출한다.
              A testB = new B();
    • 김은서

      testB는 클래스 A에 있는 메서드와 클래스 B에서 오버라이딩 한 메서드만을 사용할 수 있다.
      
      하지만 B 클래스의 데이터 타입으로 testB를 감싸며 클래스 B에서 오버라이딩 하지 않은, 클래스 B에서만 있는 메서드 또한 사용할 수 있게 되었기 때문에 더 넓은 의미의 다형성을 사용할 수 있게 된 것이다.

      참고 자료

      [Java] 메소드 오버라이딩/ 메소드 오버로딩을 통한 상속 다형성에 대한 이해와 Self 참조

profile
미래는 개발이다

0개의 댓글