[Book Review] JAVA의 신 (6)

Tony Kim·2021년 10월 13일
0
post-thumbnail

[Book Review] JAVA의 신 (6)

CH10. 상속

자바에서 상속이란?
ex. Parent & Child 두 개의 클래스 / c.inheritance라는 패키지 사용 c폴더 아래 inheritance라는 폴더 만든 후 Parent클래스의 소스가 위치해야함

package c.inheritance;
ㅤ
public class Parent {
  public Parent() {
    System.out.println(“Parent Constructor”);
  public void printName() {
    System.out.println(“Parent printName()”);
  }
}

parent클래스에는 생성자와 printName()이라는 메소드가 있다. 지금까지 봐왔던 클래스들과 다른게 없음

package c.inheritance;
ㅤ
public class Child extends Parent {
  public child() {
    System.out.println(“Child Constructor”);
  }
}

Child클래스에는 그냥 생성자만 있음 /
“Child extends Parent”
= child는 parent클래스를 상속받는다

->부모 클래스에 선언되어있는 public 및 protected로 선언되어있는 모든 변수와 메소드를 내가 갖고있는 것처럼 사용할 수 있다.

UML(unified modeling language)로 봤을 때 child가 parent를 가리키고있음

UML이란 class diagram, sequence diagram, use-case diagram 등 프로그램 만들기 전 설계

package c.inheritance;
ㅤ
public class INheritancePrint {
  public static void main(String[] args) {
    Child child = new Child();
    child.printName();
  }
}

출력

Parent Constructor
Child Constructor
Parent printName()

(Parent클래스의 메소드를 호출하지 않아도 확장한 클래스가 생성자를 호출하면 자동으로 부모 클래스의 기본생성자가 호출됨
-> 그래서 Parent Constructor라는 문장이 출력)

정리

  • 부모 클래스에서는 기본 생성자를 만들어 놓는 것 이외에는 상속을 위해서 아무런 작업을 할 필요는 없다.
  • 자식 클래스는 클래스 선언시 extends 다음에 부모 클래스 이름을 적어준다.
  • 자식 클래스의 생성자가 호출되면, 자동으로 부모클래스의 매개변수 없는 생성자가 실행된다.
  • 자식 클래스에서는 부모 클래스에있는 public, protected로 선언된 모든 인스턴스 및 클래스 변수와 메소드를 사용할 수 있다.

*상속 사용하는 이유: 하나를 제대로 만들어 놓은게 있으면 그 클래스를 상속받아 내가 추가적인 기능을 넣을 수 있음 & 각기 다른 클래스/메소드를 만들어 놨다면 수정할 때 한 번에 다 고쳐야함

*내가 이해한 것 : Child에 있는 메소드 호출 -> 생성자 메소드 호출 -> Child 메소드 실행 -> 생성자에 있지만 Child에 없는 메소드 호출

상속과 생성자
부모클래스에서는 기본 생성자를 만들어 놓는 것 이외에는 상속을 위해서 아무런 작업할 필요 없음
but, 만약 부모클래스에 기본 생성자가 없다면 ?
ChildArg() 클래스의 생성자가 실행할 때 ParentArg 클래스의 매개변수가 없는 기본 생성자를 찾는데 예시의 생성자는 String을 매개변수로 받는 생성자밖에 없기 때문
-> 그래서 부모 클래스에 “매개 변수가 없는” 기본 생성자를 만든다

OR

자식 클래스에서 부모 클래스의 생성자를 명시적으로 지정하는 super()사용

  • super()사용하면 부모클래스의 생성자를 호출한다는 것을 의미
  • 메소드처럼 사용하지 않고 super.printName() 로 사용하면 부모 클래스에 있는 printName()이라는 메소드를 호출함을 의미
    -> super(“ChildArg”) = ParentArg클래스의 생성자 중 String 타입을 매개변수로 받을 수 있는생성자를 찾음 / (기본 생성자 없어도)
  • 그렇다면 부모 생성자 중 ParentArg(매개변수가 다른 두개) 인경우 super(null)호출 할 경우?-> 에러
  • null보다 호출하고자하는 생성자의 기본타입을 넘겨주는 것이 좋음
    -> 부모 클래스에 매개 변수가 있는 생성자만 있는 경우에는 super()을 이용해서 부모 생성자를 꼭 호출
  • +자식 클래스의 생성자에서 super()를 명시적으로 지정하지 않으면 컴파일시 자동으로 super()가 추가된다. 그리고 부모 클래스의 생성자를 호출하는 super()는 반드시 자식 클래스의 생성자에서 가장 첫줄에 선언되어야함

메소드 Overriding
부모 클래스에 선언되어있는 메소드와 동일한 메소드를 자식 클래스에서 선언해서 사용할 수 있게 하는 것
1) 접근 제어자
2) 리턴 타입
3) 메소드이름
4) 매개변수 타입 및 개수가 모두 동일해야 메소드 overriding이라고 부름

parent & child 클래스에 동일한 이름/매개변수타입/리턴타입/접근제어자의 메소드가 있는경우,
Parent 클래스에 선언된 메소드가 아니라 Child 클래스에 선언된 메소드가 출력 (나머지 부모 클래스 메소드는 그대로 시행됨)
-> 생성자의 경우 자동으로 부모클래스에 있는 생성자를 호출하는 super()가 추가되지만 메소드는 그렇지 않음 = 메소드 overriding
동일하게 선언되어있다 = 동일한 시그니처 (signature)를 가진다.

overriding이 우선하기 때문에 리턴타입 / 접근제어자가 다르면 에러가 남
하지만 부모 – private / 자식 – public의 경우 가능하다
즉 자식 메소드의 접근제어자가 더 넓은 범위어야한다
( public > protected > package-private > private)

메소드 overloading : 매개변수 확장
메소드 overriding : 덮어씀 (부모 클래스의 시그니처 복제해서 자식 클래스에서 새로운 것을 만들어내어 부모 클래스의 기능 무시하고 자식클래스에 덮어씀)

참조 자료형의 형 변환
객체 생성 시
ParentCasting parent = new ParentCasting();
ChildCasting child = new ChildCasting();

ParentCasting obj = new ChildCasting(); (O)
ChildCasting obj2 = new ParentCasting(); (X)
-> 확장 VS 축소
(전자)자식 클래스 -> 부모 클래스 타입 형 변환 = 부모 클래스에서 호출할 수 있는 메소드들은 자식 클래스에서도 호출할 수 있으므로 문제 없음
(후자)부모 클래스 -> 자식 클래스 타입 형 변환 = 자식 클래스에서 호출할 수 있는 메소드들은 부모 클래스에서 호출 불가

package c.inheritance;
ㅤ
public class InheritanceCasting {
  public static void main(String[] args) {
    InheritanceCasting inheritance = new InheritanceCasting();
    Inheritance.objectCast();
  }
  public void objectCast() {
    ParentCasting parent = new ParentCasting();
    ChildCasting child = new ChildCasting();
ㅤ
    ParentCasting parent2 = child;
    ChildCasting child2 = (ChildCasting)parent;
  }
}

but 에러 : parent객체는 실제로 ParentCasting클래스의 객체이므로 컴파일 오류는 넘겼지만 “원래 ParentCasting 클래스의 객체라서 못써”라는 예외 발생
-> 그렇다면 언제 이런 명시적인 형 변환해도 문제 없는 것?

+추가

public void objectCast2() {
  ChildCasting child = new ChildCasting();
  ParentCasting parent2 = child;
// 위 둘 합쳐서 ParentCasting parent2 = new ChildCasting();
  ChildCasting child2 = (ChildCasting)parent2 
}

parent2는 child를 대입한 것 / child는 ChildCasting 클래스의 객체 / parent2의 겉모습은 ParentCasting 클래스 객체처럼 보이지만 실제로는 ChildCasting 클래스의 객체이기 때문에 parent2를 ChildCasting으로 형변환해도 문제 없음

instanceof : 타입 구분

class Parent{} class Child extends Parent{} 
public class InstanceofTest{ 
  public static void main(String[] args) {
    Parent parent = new Parent(); Child child = new Child();  
    System.out.println( parent instanceof Parent ); // true   
    System.out.println( child instanceof Parent ); // true     
    System.out.println( parent instanceof Child ); // false 
    System.out.println( child instanceof Child ); // true
  } 
}

1) parent instanceof Parent : true
2) child instanceof Parent : 상속받았기 때문에 true
3) parent instanceof Child : 역상속 관계이기 때문에 false
4) child instanceof Child : true

polymorphism (폴리몰피즘)
=다형성

package c.inheritance;
ㅤ
public class Child extends Parent{
  public ChildOther() {
  }
  public void printName() {
    System.out.println(“Child – printName()”);
  }
}
package c.inheritance;
ㅤ
public class ChildOther extends Parent{
  public ChildOther() {
  }
  public void printName() {
    System.out.println(“ChildOther – printName()”);
  }
}
public class Parent {
  public Parent() {
    System.out.println(“Parent Constructor”);
    public void printName() {
    System.out.println(“Parent printName()”);
  }
}
package c.inheritance;
ㅤ
public class InheritancePoly {
  public static void main(String args[]) {
    InheritancePoly inheritance = new INheritancePoly();
    inheritance.callPrintNames();
}
public void callPrintNamesI() {
  Parent parent1 = new Parent();
  Parent parent2 = new Child();
  Parent parent3 = new ChildOther();
ㅤ
  parent1.printName();
  parent2.printName();
  parent3.printName();
  }
}

출력

parent constructor
parent constructor
child constructor
parent constructor
childother constructor
parent printName()
parent printName()
ChildOther printName()

-> 각 객체 타입은 모두 Parent타입으로 선언되어있는데도 불구하고 printName() 메소드의 결과는 상이하다. 즉 선언시에는 모두 parent타입이지만 실제로 호출된 메소드는 생성자를 사용한 클래스에 있는 것이 호출됨 -> 왜냐하면 각 객체의 실제 타입이 다르기 때문
= 형변환을 하더라도 실제 호출되는 것은 원래 객체에 있는 메소드가 호출된다 = polymorphism

profile
Back-end-dev

0개의 댓글