[KOSTA JAVA] #Day 7 (Inheritance extends super, Override, Polymorphism, Promotion, Abstract class)

0f1c04·2021년 2월 23일
0

KOSTA JAVA TIL

목록 보기
6/11
post-thumbnail

신용권님의 ''이것이 자바다'' 공부 기록

책을 보면서 내용을 정리했습니다. 이것이 자바다 커뮤니티 : https://cafe.naver.com/thisisjava

1. 상속(Inheritance)이란

현실 세계에서 부모가 자식에게 물려주는 행위를 말한다. 현실 세계에서는 부모가 자식을 선택한다. 객체 지향 프로그램에서 상속이란 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것을 말한다. 현실 세계와는 다르게 자식이 부모를 선택해서 물려받을 수 있으며, 부모의 필드와 메소드가 상속대상이다.

1.1 상속(Inheritance) 개념의 활용

  • 상속의 효과
    • 부모 클래스를 재사용하여 자식 클래스를 빠르게 개발이 가능하다.
    • 반복된 코드 중복을 줄인다.
    • 유지 보수의 편리성을 제공한다.
    • 객체 다형성을 구현할 수 있다.
  • 상속 대상 제한
    • 부모 클래스의 private 접근제한자를 갖고 있는 필드와 메소드는 상속이 제한된다.
    • 부모 클래스가 다른 패키지에 있을 경우, default 접근제한자를 갖는 필드와 메소드도 제외한다.

2. 클래스 상속(extends)

  • 자식 클래스가 상속할 부모 클래스를 지정하는 키워드

  • 자바는 단일 상속으로 다수의 부모 클래스를 나열할 수 없다.

    class ChildClass extends SuperClass1, SuperClass2 {...} // 불가
    
    class ChildClass extends SuperClass1 {...} // 단일상속

3. 부모 생성자 호출

3.1 자식 객체를 생성하면 부모 객체도 생성되는가

  • 자식 객체를 생성할 때는 부모 객체로부터 생성 후 자식 객체를 생성한다.
  • 부모 생성자를 호출 완료 후 자식 생성자 호출을 완료한다.

3.2 명시적인 부모 생성자 호출

  • 부모 객체를 생성할 때, 부모 생성자를 선택하여 호출할 수 있다.

    ChildClass(parameter, ...) {
      super(parameter, ...);
      ...
    }
  • super(매개값, ...): 매개값과 동일한 타입, 개수, 순서가 맞는 부모 생성자를 호출한다.

  • 부모 생성자가 없다면 컴파일 오류가 발생한다.

  • super()은 반드시 자식 생성자의 첫 줄에 위치하여야한다.

  • 부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수로 작성하여야 한다.


4. 메소드 재정의(Override)

부모 클래스의 상속 메소드를 수정하여 자식 클래스에서 재정의하는 것을 말한다. 재정의 조건으로는 부모 클래스의 메소드와 동일한 시그니쳐를 가져야하며 접근 제한자를 더 강하게는 재정의가 불가하다.

4.1 @Override 어노테이션

  • 컴파일러에게 부모 클래스의 메소드 선언부와 동일한지 검사를 지시한다.
  • 정확한 메소드 재정의를 위해 붙여주면된다.

4.2 메소드 재정의 효과

  • 재정의된 자식 메소드가 실행됨에 따라 부모 메소드는 숨겨지는 효과가 있다.

4.3 부모 메소드 사용(super)

  • 자식 클래스에서 수정되기 전 부모 메소드를 호출(super)할 수 있다. (this는 자신 객체 참조, super은 부모 객체 참조)

5. final 클래스와 final 메소드

5.1 final 키워드의 용도

  • final 필드: 수정 불가 필드
  • final 클래스: 부모로 사용 불가한 클래스
  • final 메소드: 자식이 재정의할 수 없는 메소드

5.2 상속할 수 없는 final 클래스

  • 자식 클래스 만들지 못하도록 final 클래스로 생성: public final class ClassName {...}

5.3 오버라이딩 불가한 final 메소드

  • 자식 클래스가 재정의 못하도록 부모 클래스의 메소드를 final로 생성

6. protected 접근 제한자

상속과 관련된 접근 제한자로 같은 패키지내에선 default 접근제한자와 동일하고, 다른 패키지에서는 자식 클래스만 접근을 허용한다.


7. 타입변환과 다형성(polymorphism)

7.1 다형성(Polymorphism)

같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질을 다형성이라한다. 부모 타입에는 모든 자식 객체가 대입 가능하다. (자식 타입은 부모 타입으로 자동 타입 변환) 다형성을 활용하면 객체 부품화가 가능하다.

7.2 자동 타입 변환(Promotion)

  • 프로그램 실행 도중에 자동 타입 변환이 일어나는 것을 말한다.
  • 바로 위의 부모가 아니더라도 상속 계층의 상위면 자동 타입 변환이 가능하다. 변환 후에는 부모 클래스 멤버만 접근 가능하다.

7.3 필드의 다형성

  • 다형성을 구현하는 기술적인 방법으로는 부모 타입으로 자동 변환과, 재정의된 메소드(오버라이딩)이 있다.

7.4 매개변수의 다형성

  • 매개변수가 클래스 타입일 경우 해당 클래스의 객체 대입이 원칙이나 자식 객체를 대입하는 것도 허용하여 자동 타입 변환과 매개변수의 다형성을 볼 수 있다.

7.5 강제 타입 변환(Casting)

  • 부모 타입을 자식 타입을으로 변환하는 것을 말한다.
  • 자식클래스 변수 = (자식클래스) 부모클래스타입; //강제 타입 변환
  • 조건으로 자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때 사용한다.
  • 강제 타입 변환이 필요한 경우
    • 자식 타입이 부모 타입으로 자동 변환될 때 부모 타입에 선언된 필드와 메소드만 사용 가능하다.
    • 자식 타입에 선언된 필드와 메소드를 다시 사용해야할 경우

7.6 객체 타입 확인(instanceof)

  • 부모 타입이면 모두 자식 타입으로 강제 타입 변환할 수 있는 것은 아니다.

    • ClassCastException 예외가 발생할 수 있다.

      Parent parent = new Parent();
      
      Child child = (Child)parent; 		//강제 타입 변환을 할 수 없다.
  • 먼저 자식 타입인지 확인 후 강제 타입을 실행하여야 한다.

    boolean result = (객체) instanceof (타입)

    public void method(Parent parent) {
      if(parent instanceof Child) {
        Child child = (Child) parent;
      }
    }

Account 다형성, 타입 변환 예제

package com.kosta.day07;

//Account 다형성연습
public class AccountTest2 {
    public static void main(String[] args) {
        //계좌 4개 만들기: 배열사용
        //Account
        //CreditLineAccount
        //BonusAccount
        //CheckingAccount

        Account[] accounts = new Account[4];
        makeAccount(accounts);
        printAccount(accounts);
        System.out.println("---------Casting Account");
        castingAccount(accounts);
    }

    private static void castingAccount(Account[] accounts) {
        for(Account arr:accounts) {
            if(arr instanceof CreditLineAccount) {
                System.out.println("=========Casting CreditLineAccount");
                //new CreditLineAccount() -> Account -> CreditLineAccount
                CreditLineAccount creditLineAccount = (CreditLineAccount) arr;
                System.out.println("마이너스한도: " + creditLineAccount.getCreditLine());
            }else if(arr instanceof BonusAccount) {
                System.out.println("=========Casting BonusAccount");
                //new BonusAccount() -> Account -> BonusAccount
                arr.deposit(1000);
                BonusAccount bonusAccount = (BonusAccount) arr;
                bonusAccount.deposit(2000);
                System.out.println(bonusAccount.bonusPoint + " 포인트");
            }else if(arr instanceof CheckingAccount) {
                System.out.println("=========Casting CheckingAccount");
                //new CheckingAccount() -> Account -> CheckingAccount
                CheckingAccount checkingAccount = (CheckingAccount)arr;
                System.out.println("카드번호: " + checkingAccount.getCardNo());
                int amount = checkingAccount.pay("0212", 100);
                System.out.println(amount);
            }
        }
    }

    private static void printAccount(Account[] accounts) {
        for(Account arr:accounts) {
            System.out.println(arr);
            System.out.println("==========================================");
        }
    }

    private static void makeAccount(Account[] accounts) {
        //자동형변환: 부모타입 = 자식객체
        accounts[0] = new Account("111", "kim", 100);
        accounts[1] = new CreditLineAccount("222", "lim", 100, 500);
        accounts[2] = new BonusAccount("333", "bae", 100, 10);
        accounts[3] = new CheckingAccount("444", "park", 100, "0212");
    }
}

8. 추상 클래스(Abstract Class)

8.1 추상 클래스 개념

추상(abstract)란 실체들 간에 공통되는 특성을 추출한 것을 말한다. 예로 새, 곤충, 물고기를 보고 동물이란 것을 추상하듯이 이러한 특성을 추출하는 것을 추상이라 한다. 추상 클래스(abstract class)는 실체 클래스들의 공통되는 필드와 메소드를 정의한 클래스를 말한다. 추상 클래스는 실체 클래스의 부모 클래스 역할을 한다. 그렇다고 단독 객체는 아니다.

8.2 추상 클래스의 용도

  • 실체 클래스를 설계하는 설계자가 여러 사람일 경우, 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있다. 이런 혼란을 방지하기 위해 실체 클래스의 공통된 필드와 메소드의 이름을 통일할 목적
  • 실체 클래스는 추가적인 필드와 메소드만 선언하여 실체 클래스를 작성할 때 시간을 절약할 수 있다.
  • 실체 클래스가 가져야 할 필드와 메소드를 추상 클래스에 미리 정의하고 실체 클래스는 추상 클래스를 무조건 상속 받아 작성하게 하여 실체 클래스 설계 규격을 만들고자 할 때 사용한다.

8.3 추상 클래스 선언

  • New 연산자로 객체를 생성하지 못하고 상속을 통해 자식 클래스만 생성할 수 있다.

  • 클래스 선언시 abstract 키워드를 사용한다

    public abstract class ClassName {
      //field
      //constructor
      //method
    }

8.4 추상 메소드와 오버라이딩(재정의)

  • 메소드 이름은 동일하지만, 실행 내용이 실체 클래스마다 다른 메소드를 재정의된 메소드라 말한다.

  • 구현 방법

    • 추상 클래스에는 메소드의 선언부만 작성(추상 메소드)
    • 실체 클래스에서 메소드의 실행 내용 작성(Overriding)
    //추상 클래스
    public abstract class Animal {
      public abstract void sound();
    }
    //실체 클래스
    public class Dog extends Animal {
      @Override
      public void sound() {
        System.out.println("bow bow");
      }
    }
    //실체 클래스
    public class Cat extends Animal {
      @Override
      public void sound() {
        System.out.println("meow meow");
      }
    }

추상 예제

출력 결과 >>

Shape 0 is a BLUE Circle with an area of 314.15927, a radius of 10.0, and a perimeter of 62.831854

Shape 1 is a RED Rectangle with an area of 35.0 and a perimeter of 24.0

Shape 2 is a GREEN Circle with an area of 201.0619328, a radius of 8.0, and a perimeter of 50.2654832

Abstract class: Shape

package com.kosta.day07;

/*
abstract class: abstract method가 하나이상 존재한다.
abstract method: 함수의 정의는 있으나 구현은 없다. > 구현은 해당 class를 상속받은 자식클래스에서 반드시 한다.
*/

public abstract class Shape {
    private String color;
    private String type;

    public Shape(String color, String type) {
        this.color = color;
        this.type = type;
    }

    //--------------------------------규격--------------------------------
    //calculateArea: 면적구하기
    public abstract double calculateArea(); //정의는 있고 구현은 없음 (추상 메소드)
    //어떤도형인지 결정되지않아 면적구하기 불가

    //calculatePerimeter(): 둘레구하기
    public abstract double calculatePerimeter(); //정의는 있고 구현은 없음 (추상 메소드)
    //어떤도형인지 결정되지않아 둘레구하기 불가
    //-------------------------------------------------------------------

    public String getColor() {
        return color;
    }
    public String getType() {
        return type;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append(color).append(' ');
        sb.append(type);

        return sb.toString();
    }

}

Circle class

package com.kosta.day07;

public class Circle extends Shape{
    int radius;
    double circumference;
    Circle(String color, int radius) {
        super(color, "Circle");
        this.radius = radius;
    }

    double getRadius() {
        return radius;
    }

    @Override
    public double calculateArea() {
        return radius*radius*Math.PI;
    }

    @Override
    public double calculatePerimeter() {
        circumference = 2*radius*Math.PI;
        return circumference;
    }
}

Rectangle class

package com.kosta.day07;

public class Rectangle extends Shape{
    int width;
    int height;
    public Rectangle(String color, int width, int height) {
        super(color, "Rectangle");
        this.width = width;
        this.width = height;
    }

    @Override
    public double calculateArea() {
        return width * height;
    }

    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }
}

Application(main): TestShape class

package com.kosta.day07;

public class TestShape {

    public static void main(String args[]) {
        Shape[] s = new Shape[3];

        s[0] = new Circle("BLUE", 10);
        s[1] = new Rectangle("RED", 5, 7);
        s[2] = new Circle("GREEN", 8);

        for (int i = 0; i < 3; i++) {
            if (s[i] instanceof Circle) {
                System.out.println("Shape " + i + " is a " + s[i]
                        + " with an area of " + s[i].calculateArea()
                        + ", a radius of " + ((Circle) s[i]).getRadius()
                        + ", and a perimeter of " + s[i].calculatePerimeter());
            } else if (s[i] instanceof Rectangle) {
                System.out.println("Shape " + i + " is a " + s[i]
                        + " with an area of " + s[i].calculateArea()
                        + " and a perimeter of " + s[i].calculatePerimeter());
            }
        }
    }
}
profile
라면 먹고 싶다. 두 개 끓여서 혼자 먹고 싶다. - 임덕배 (1997. 06 ~ )

0개의 댓글