[자바와 객체 지향 그리고 스프링] 3- 상속: 재사용과 확장

코린이서현이·2024년 1월 1일
0

🎯 목표

📌 상속에 대해서 정확히 이해하자.
📌 상속의 예제 코드를 살펴보자.
📌 다중상속을 지원하지 않는 자바의 인터페이스를 살펴보자.
📌 T메모리와 함께 살펴보자.

📌 상속

자바에서 상속은 inheritance(상속) 가 아닌 extends(확장)이라는 예약어를 사용한다.

객체 지향에서의 상속

상속상위 클래스의 특성을 하위 클래스에서 재사용하고, 필요한 특성을 추가하여 확장해 사용한다.

  • 하위 클래스는 상위 분류(클래스)의 확장이자, 세분화 된 것이다.
  • 하위 클래스는 상위 클래스의 모든 특성을 재사용하고, 추가로 확장한다.
  • 하위 클래스는 구체화, 특수화되어있다.
  • 상위 클래스는 추상화, 일반화되어있다.
  • ⭐ 하위 클래스는 상위클래스이다.

🤔 상속을 계층이 아니라 포함의 다이어그램으로 이해하자.

상속이라고 생각하면 생물 파트의 유전자 문제의 조직도, 계층도가 생각난다.
그런데 상속은 그것보다는 ... 🤔 병합된 엑셀표로 생각해보자!!

포함으로 이해하면 적절한 이유

객제 지향의 상속은 하위클래스가 상위클래스에 포함된다.
예를 들어 동물클래스를 상속받은 포유류는 동물의 속성을 모두 재사용하면서, 추가적으로 젖을 물리는 행위를 추가해서 확장한다. 이때 포유류는 동물에 포함되면서, 동물인 것이다.

그러나 가족 구성원을 나타내는 데 쓰이는 계층 구조로는 이런 포함의 관계를 설명하고 직관적으로 이해하기 어렵다.
조직도에서는 자식이 부모에 포함되고, 부모인 것은 아니기 때문이다. 그래서 혹시 상속이란 개념이 헷갈린다면 포함의 벤 다이어그램으로 이해하는 것은 어떨까?

동물 로아 = new 포유류();
// 포유류라는 객체 인스턴스를 동물 로아에 집어넣었다.
// 즉 로아는 동물이다.

🙆 상속은 is a kind of 관계를 만족해야한다.

보통 상속 관계를 표현하는 is a를 더 많이 들어봤을 것이다.
하지만 상속관계를 더 명확히 하는 표현은 종류를 뜻하는 kind of가 더 적절하다.

😎 펭귄 is a kind of 조류
➡️ 펭귄은 조류의 한 종류이다.

📌 상속과 실제 코드

🤔 상속이 왜 객체 지향의 특성일까?

객체 지향은 인간이 현실을 인식하는 논리대로 프로그래밍 할 수 있게 해준다.

🤔 사람은 박쥐, 고래, 참새를 어떻게 인식할까?
👤 박쥐는 "조류"이면서, "동물"이고, "생물"이에요!!

즉, 박쥐는 생물 분류 내, 동물 분류 내, 조류이다.

➕ 더 나아가면? 박쥐를 조류로도 인식하고, 동물로도 인식한다!!
➡️ 이런 인간의 인식 방법을 프로그래밍으로 가능하게 하는 것이 "상속"이다!!

동물 박쥐A= new 조류();
//프로그래밍인 박쥐를 동물로 인식한다.
//🤔... 코드로 살펴보자 ⬇️⬇️

실제 코드

클래스 다이어그램

🤔 박쥐, 고래, 참새, 펭귄은 모두 동물!!

클래스 상속

package OOP03.inheritance01;

public class 동물 {
  String myClass;

  public 동물() {
    myClass = "동물";
  }

  void showMe() {
    System.out.println("myClass = " + myClass);
  }
}
package OOP03.inheritance01;

public class 포유류 extends 동물 {
  public 포유류() {
    myClass = "포유류";
  }
}
package OOP03.inheritance01;

public class 고래 extends 포유류 {
  public 고래() {
    myClass = "고래";
  }
}
package OOP03.inheritance01;

public class 펭귄 extends 조류 {
  public 펭귄() {
    myClass = "펭귄";
  }
}
package OOP03.inheritance01;

public class 조류 extends 동물 {
  public 조류() {
    myClass = "조류";
  }
}
package OOP03.inheritance01;

public class 박쥐 extends 포유류 {
  public 박쥐() {
    myClass = "박쥐";
  }
}
package OOP03.inheritance01;

public class 참새 extends 조류 {
  public 참새() {
    myClass = "조류";
  }
}

😎 실제 구현을 통해 강점을 알아보자!!

  • 특성을 물려줄 수 있어서, 작성을 줄여준다. 재사용이 가능하다.
  • 인간의 논리를 프로그래밍에 적용시킨다.
  • 각각의 하위 클래스를 상위클래스로 반응(..?)하게 할 수 있다.

특성을 물려줄 수 있어서, 작성을 줄여준다. 재사용이 가능하다.

하위클래스에 showMe를 따로 작성하지 않아도 재사용이 가능했다.
하위클래스 조류,포유류는 모두 동물이니까!

package OOP03.inheritance01;

public class Driver01 {
  public static void main(String[] args) {
    // 😎 우와 extends를 쓰니까 재사용할 수 있네!! showMe를 하나하나 만들지 않아도 돼!!
    동물 animal = new 동물();
    포유류 mammalia = new 포유류();
    조류 bird = new 조류();
    고래 whale = new 고래();
    박쥐 bat = new 박쥐();
    참새 sparrow = new 참새();
    펭귄 penguin = new 펭귄();

    animal.showMe();
    mammalia.showMe();
    bird.showMe();
    whale.showMe();
    bat.showMe();
    sparrow.showMe();
    penguin.showMe();

    //🤔 흠... 근데 하위 클래스 모두 상위 클래스이잖아.
    //객체 참조 변수는 모두 동물이잖아!!
  }
}

인간의 논리를 프로그래밍에 적용시킨다.

각각의 하위 클래스는 모두 상위클래스이다.

package OOP03.inheritance01;

public class Driver02 {
  public static void main(String[] args) {
    //😎 하위 클래스는 모두?? 상위 클래스
    동물 animal = new 동물();
    동물 mammalia = new 포유류();
    동물 bird = new 조류();
    동물 whale = new 고래();
    동물 bat = new 박쥐();
    동물 sparrow = new 참새();
    동물 penguin = new 펭귄();

    animal.showMe();
    mammalia.showMe();
    bird.showMe();
    whale.showMe();
    bat.showMe();
    sparrow.showMe();
    penguin.showMe();

    //🤔 흠... 객체 참조 변수명은 다 다르지만 타입은 동물로 모두 동일하네..?
    // 이 변수를 모두 한번에 사용할 수 있지 않을까?
    }
  }

각각의 하위 클래스를 상위클래스로 반응(..?)하게 할 수 있다.

헉 이건 진짜 좋당~

package OOP03.inheritance01;

public class Driver03 {
  public static void main(String[] args) {
    동물[] animals = new 동물[7];

    animals[0] = new 동물();
    animals[1] = new 포유류();
    animals[2] = new 조류();
    animals[3] = new 고래();
    animals[4] = new 박쥐();
    animals[5] = new 참새();
    animals[6 ] = new 펭귄();

    for (int index = 0; index < animals.length; index++) {
      animals[index].showMe();
    }
  }
}

📌 인터페이스

자바에서 다중상속이 가능한가?

🙅‍ 클래스끼리의 다중 상속은 지원하지 않는다.
🙆‍♂️ 인터페이스를 통해 다중 구현을 지원한다.

➕ 자바에서 다중 상속을 지원하지 않는 경우는 포스팅했던 것 같다. 해당 글 보러가기
흠... 너무 짧게 포스팅했나..? 하여튼... 득보다 실이 많아서 과감하게 다중상속을 금지했다.

인터페이스는 is able to로 이해하자!

  • 인터페이스는 해당 클래스가 인터페이스의 특성을 할 수 있음을 나타낸다.
    ➡️ 상속이 분류였다면, 인터페이스는 분류로는 생각할 수 없다.
  • 인터페이스는 구현 클래스가 해당 기능을 필수로 구현해야함을 강제한다.
  • 인터페이스는 다중상속을 구현한다.

즉, 해당 클래스가 해당 메서드를 구현해야 하고, 그러한 클래스가 상위-하위 관계가 아닐 때 인터페이스를 활용할 수 있다.

사실 인터페이스는 더 세세한 내용이 많다. 그리고 심화적인 이야기도 해야한다. "인터페이스 분할 원칙"이라는 것도 있고... 그렇지만 세세한 내용은 중간 단계보다는기초 단계의 글을 확인하길 바라고, 심화적인 부분은 조금 더 나중에 이야기하겠다! (사실 내가 아직 공부 안함, 그런데 중요한건 기억남ㅎ)

상위클래스는 하위클래스에게 물려줄 특성이 많을수록 좋다.
인터페이스는 각각 구현을 강제할 메서드가 적을 수록 좋다.

실제 코드

package OOP03.inheritance01;

public interface 헤엄칠수있는 {
  void swim();
}
package OOP03.inheritance01;

public class 고래 extends 포유류 implements 헤엄칠수있는 {
  public 고래() {
    myClass = "고래";
  }

  @Override
  public void swim() {
    System.out.println("🐋 🏊‍♂️");
  }
}
package OOP03.inheritance01;

public class 펭귄 extends 조류 implements 헤엄칠수있는 {
  public 펭귄() {
    myClass = "펭귄";
  }

  @Override
  public void swim() {
    System.out.println("🐧 🏊‍♂️");
  }

}
package OOP03.inheritance01;

public class Driver04 {
  public static void main(String[] args) {
    헤엄칠수있는[] ableToSwim = new 헤엄칠수있는[2];

    ableToSwim[0] = new 고래();
    ableToSwim[1] = new 펭귄();

    for (int index = 0; index < ableToSwim.length; index++) {
      ableToSwim[index].swim();
    }
  }
}

📌 상속과 T메모리

코드

package OOP03.inheritance03;

public class Anlimal {
  public String name;

  public void showName() {
    System.out.println("나는 " + name + "(이)야. 반가워");
  }
}
package OOP03.inheritance03;

public class Penguin extends Anlimal{
  public String habitat;

  public void showHabitat() {
    System.out.printf("%s는 %s에 살아\n", name, habitat);
  }
}
package OOP03.inheritance03;

public class Driver {
  public static void main(String[] args) {
    Penguin proro = new Penguin();	
    //Aaimal 클래스의 인스턴스도 함께 힙영역에 생성된다.
    //📌 하위 클래스의 인스턴스가 생성될 때 상위클래스의 인스턴스도 함께 생성된다. 
    
    proro.name = "뽀로로";
    proro.habitat = "눈 덮인 숲속 마을";

    proro.showName();
    proro.showHabitat();

    Anlimal pingu = new Penguin();
    //Aaimal 클래스의 인스턴스도 함께 힙영역에 생성된다.
    //📌 하위 클래스의 인스턴스가 생성될 때 상위클래스의 인스턴스도 함께 생성된다. 
    //📌 그러나 객체 참조 변수의 타입이 Ainmal로 암묵적 형변환이 일어난다.  
    // 암묵적 형변환의 경우 : 생성클래스의 멤버 변수, 메소드 👉 ❌

    pingu.name = "핑구";
//    pingu.habitat = "눈 덮인 숲속 마을";

    pingu.showName();
//    pingu.showHabitat();
  }
}

T 메모리

📌 하위 클래스의 인스턴스가 생성될 때 상위클래스의 인스턴스도 함께 생성된다.
따라서 뽀로로가 참조하고 있는 인스턴스는 Penguin 인스턴스와 Animal 인스턴스가 함께 생성된다.

🤔 그러면 핑구는?
핑구 역시 하위클래스의 생성자로 인스턴스가 생성되면서 Penguin 인스턴스와 Animal 인스턴스가 함께 생성된다.
📌 그러나 객체 참조 변수의 타입이 Ainmal로 암묵적 형변환이 일어난다.
따라서 인스턴스는 Peguin인스턴스도 생성되었다고 해서 사용할 수는 없다.

👉 암묵적 형변환
😅 지금보니까 조금 대충 정리하기는 했네...ㅎ

📌 암묵적 형변환이란
- 자식클래스가 부모클래스에게 상속받은 기능만 사용하도록 제한
- 기능의 제한으로 하위클래스의 멤버 사용 불가능
- 기능의 변경은 아님 : 오버라이딩된 하위클래스의 메서드 사용 가능

🤔마무리하면서🤔

기억하고 싶은 것!!

📌 하위 클래스의 인스턴스가 생성될 때 상위클래스의 인스턴스도 함께 생성된다.
📌 그러나 객체 참조 변수의 타입이 Ainmal로 암묵적 형변환이 일어난다.

사실 이렇게 끝내면 의아한 사람이 많을 것이다!
상위클래스와 하위클래스의 묵시적 형변환을 하면 짝꿍으로 나오는게 메서드 오버라이딩이니까!!
그건 다음 글에서 포스팅하겠다~!
profile
24년도까지 프로젝트 두개를 마치고 25년에는 개발 팀장을 할 수 있는 실력이 되자!

0개의 댓글