자바 기본 복습 6. 상속

장난·2021년 5월 21일
0

자바 기본

목록 보기
6/15
post-thumbnail

6주차 과제: 상속


📌 목표

자바의 상속에 대해 학습하세요.


📌 학습할 것

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

📜 시작에 앞서

  • 백기선 님의 라이브 스터디(2020년 11월부터 2021년 3월까지) 커리큘럼을 따라 진행한 학습입니다
  • 뒤늦게 알게 되어 스터디 참여는 못했지만 남아있는 스터디 깃허브 주소유튜브 영상을 참고했습니다

📑 자바 상속의 특징

  • class SubClass extends SuperClass{} 형식으로 상속
  • 단일 상속만 허용 extends A, B 불가
  • 자식 클래스는 부모 클래스의 모든 멤버 상속
    • 단, 접근 제한자에 의한 접근 고려 (private이나 다른 패키지에 있는 default)
  • 부모 클래스의 생성자와 초기화 블록은 상속 X
  • 상속 횟수에 제한이 없으며, 모든 클래스의 최상위 클래스는 Object
  • 상속관계에 있는 클래스간 형변환 가능
    • 부모 클래스 -> 자식 클래스, 자식 클래스 -> 부모 클래스, 자식 클래스 -> ... -> 부모 클래스

📑 super 키워드


super

  • 부모 클래스에 대한 참조값을 저장하고 있는 하위 클래스의 참조변수
  • super.superVar super.superMethod()

super()

  • 부모 클래스의 생성자를 호출

  • 자식 클래스가 생성될 때는 부모 클래스의 생성자가 먼저 호출

    • 자식 클래스의 생성자에 대해 명시적으로 부모 클래스의 생성자를 호출하지 않는다면 컴파일러가 자동으로 super() 추가 (부모 클래스의 기본 생성자 호출)

sub-class


📑 메소드 오버라이딩


오버라이딩

  • 부모 클래스로부터 상속 받은 메서드의 구현부를 자식 클래스에서 변경
    • 메서드 선언부가 조성 클래스의 메서드와 같아야 한다
    • 부모 클래스의 메서드의 접근 제한자보다 좁은 범위의 접근 제한자 설정 불가
      • 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다는 리스코프 치환 원칙(LSP)을 지키기 위함
    • 부모 클래스의 메서드가 던지는 예외보다 더 많은 예외를 던질 수 없다
      • 이때 더 많은 예외란 ,로 구분해 던지는 예외 수가 아니라 계층 구조상 하부에 있는 예외 수기 때문에 throws Exception 으로 던진다면 이는 모든 예외의 최고 조상이므로 던질 수 있는 모든 수의 예외를 던진 것

@Override

  • 컴파일러에게 오버라이드한 메서드라는 정보 제공
    • 컴파일러가 오버라이드 가능한지 검증하고 불가능하다면 컴파일 에러

📑 다이나믹 메서드 디스패치 (Dynamic Method Dispatch)


메서드 디스패치 (Method Dispatch)

  • 메시지에 대한 응답으로 호출할 메서드를 결정하고 실행시키는 과정

스태틱 메서드 디스패치 (Static Method Dispatch)

  • 컴파일 타임에 어떤 메서드를 호출했을 때 어떤 메서드 구현부(바디)를 실행할지 알고 있는 것
  • 자바에서는 모든 메서드에 대해 다이나믹 메서드 디스패치 사용
    • :interrobang: 자바에서도 컴파일러가 어떤 메서드 쓸지 확실히 알 수 있게 작성된 코드는 메서드 스태틱 디스패치 된다는 글도 많고 아니라는 글도 많아서 헷갈린다..
  • 자바에서 private final static 각각이나 이들의 조합으로 오버라이드를 막을 수는 있겠으나 스태틱 메서드 디스패치로 실행은 불가능

다이나믹 메서드 디스패치 (Dynamic Method Dispatch)

  • 비슷한 용어로 dynamic dispatch, run-time dispatch, virtual method call, late binding
  • 어떤 메서드를 호출했을 때 어떤 메서드 구현부(바디)를 실행할지에 대한 결정을 런타임으로 미루는 것
  • 모든 OOP 언어는 어떤 형태로든 다이나믹 메서드 디스패치 기능을 지원
    • 외부의 요청이 무엇인지를 표현하는 메시지와 요청을 처리하기 위한 구체적인 방법인 메서드를 분리하는 것은 객체의 자율성을 높이는 메커니즘이다. 이것은 캡슐화라는 개념과도 깊이 관련돼 있다.*
    • 캡슐화 관련? : 메시지를 결정하는 시점에서 어떤 객체가 메시지를 수신할지 모르기 때문에, 송신자는 수신자 객체의 내부 상태를 볼 수 없다.

*매서드와 자율성

객체는 다른 객체와 협력하기 위해 메시지를 전송한다. 수신자는 먼저 수신된 메시지를 이해할 수 있는지 여부를 판단한 후 미리 정해진 자신만의 방법에 따라 메시지를 처리한다. 이처럼 객체가 수신된 메시지를 처리하는 방법을 메서드라고 부른다.

객체지향 프로그래밍 언어에서 메서드는 클래스 안에 포함된 함수 또는 프로시저를 통해 구현된다. 따라서 어떤 객체에게 메시지를 전송하면 결과적으로 메시지에 대응되는 특정 메서드가 실행된다. 메시지를 수신한 객체가 실행 시간에 메서드를 선택할 수 있다는 점은 다른 프로그래밍 언어와 객체지향 프로그래밍 언어를 구분 짓는 핵심적인 특징 중 하나다. 이것은 프로시저 호출에 대한 실행 코드를 컴파일 시간에 결정하는 절차적인 언어와 확연히 구분되는 특징이다.

외부의 요청이 무엇인지를 표현하는 메시지와 요청을 처리하기 위한 구체적인 방법인 메서드를 분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘이다. 이것은 캡슐화라는 개념과도 깊이 관련돼 있다.

출저: 객체지향의 사실과 오해, 조영호

  • 런다임에 같은 메시지에 대해 다른 유형의 객체가 서로 다르게 반응할 수 있는 것이기 때문에 다형성과도 관련

public class DMD {
    public static void main(String[] args) {
        Shape triangle = new Triangle();
        Shape square = new Square();

        triangle.printInfo();
        square.printInfo();
    }

    interface Shape {
        public void printInfo();
    }

    static class Triangle implements Shape {
        @Override
        public void printInfo() {
            System.out.println("Triangle");
        }
    }

    static class Square implements Shape {
        @Override
        public void printInfo() {
            System.out.println("Square");
        }
    }
}

/*
실행결과
Triangle
Square
*/
  • 여기서는 인터페이스를 예로 했으나 클래스나 추상클래스를 상속할 경우에도 같은 결과

더블 디스패치 (Double Dispatch)

  • 다이나믹 디스패치를 두 번 한 것
  • 자바는 싱글 디스패치 언어기 때문에 이를 구현하려면 특정 방법 필요
  • 그 중 하나인 방문자 패턴

Double-Dispatch

  • Painter = Visitor
  • Shape = Element

public class DD {
    public static void main(String[] args) {

        Painter redPainter = new RedPainter();
        List<Shape> shapes = Arrays.asList(new Triangle(), new Square());

        for (Shape shape : shapes) {
            shape.paintBy(redPainter);
        }
    }
}

interface Painter {
    void paintOn(Triangle triangle);
    void paintOn(Square square);
}

class RedPainter implements Painter {

    @Override
    public void paintOn(Triangle triangle) {
        System.out.println("RedPainter paint triangle");
    }

    @Override
    public void paintOn(Square square) {
        System.out.println("RedPainter paint square");
    }

}

class GreenPainter implements Painter {
    @Override
    public void paintOn(Triangle triangle) {
        System.out.println("GreenPainter paint triangle");
    }

    @Override
    public void paintOn(Square square) {
        System.out.println("GreenPainter paint square");
    }
}

interface Shape {
    public void paintBy(Painter painter);
}

class Triangle implements Shape {

    @Override
    public void paintBy(Painter painter) {
        painter.paintOn(this);
    }

}

class Square implements Shape {
    @Override
    public void paintBy(Painter painter) {
        painter.paintOn(this);
    }
}

/*
실행결과
RedPainter paint triangle
RedPainter paint square
*/
  • Painter = Visitor
  • Shape = Element
  • Element에 대해 미리 알고 있을 때, Visitor 확장 쉽다
  • 런타임 중에 Element, Visitor의 어떤 구현체를 사용할지 몰라도, 정확한 메서드를 호출할 수 있다

📑 추상 클래스


추상 클래스

abstract class AbstractClass{
    ...
    //추상 메서드
    abstract 메서드 선언부();
}
  • 추상 메서드를 포함하는 클래스
  • 인스턴스 생성 불가
  • 생성자, 멤버변수, 메서드 포함 가능

📑 final 키워드


final

  • 해당 엔티티가 한반만 할당될 수 있다는 의미의 예약어
  • 클래스, 메서드, 변수에 사용 가능
대상설명
클래스상속할 수 없는 클래스
메서드재정의할 수 없는 메서드
변수초기화하고 나면 값을 변경할 수 없는 상수
  • final 변수 초기화는 선언과 동시 or 생성자에서 초기화

static

(나중에 나올 곳 애매해서 그냥 여기..)

  • 클래스가 메모리에 로드될 때 저장
대상설명
멤버변수인스턴스 생성하지 않고 공통으로 사용 가능
메서드인스턴스 생성하지 않고 호출 가능
중첩 클래스정적 중첩 클래스

📑 Object 클래스

  • java8 docs: Class Object
  • 모든 클래스의 최상위 클래스
    • 모든 클래스는 Object 를 상속하며, 상속하는 클래스가 없는 클래스를 작성해도 extends Object 를 컴파일러가 자동으로 추가
    • 인스턴스의 유형을 알지 못해도 Object 선언을 사용해 객체 전달 가능
    • 모든 클래스에 대한 공통 청사진, Object 의 메서드 참고
  • 기본이 되는 클래스기 때문에 java.laing 패키지에 존재
  • 멤버변수 없이 11개의 메서드로 구성

Modifier and TypeMethod and Description
protected Objectclone()Creates and returns a copy of this object.
booleanequals(Object obj)Indicates whether some other object is "equal to" this one.
protected voidfinalize()Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
Class<?>getClass()Returns the runtime class of this Object.
inthashCode()Returns a hash code value for the object.
voidnotify()Wakes up a single thread that is waiting on this object's monitor.
voidnotifyAll()Wakes up all threads that are waiting on this object's monitor.
StringtoString()Returns a string representation of the object.
voidwait()Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
voidwait(long timeout)Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
voidwait(long timeout, int nanos)Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

출저 : java8 docs: Class Object


equals()

public boolean equals(Object obj) {
    return (this == obj);
}
  • 기본적으로 두 객체의 참조변수로 판단
  • 논리적으로 같고 다름을 나타내는 기준이 다르다면 재정의 필요

hashCode()


toString()

  • 기본적으로 객체의 getClass().getName() + '@' + Integer.toHexString(hashCode())반환
  • 객체의 정보를 적절한 문자열로 반환하고자 재정의

clone()

  • 기본적으로 해당 인스턴스 복제한 인스턴스 반환
    • 이때 기본적으로 참조 타입에 대해서 주소값을 복사하는 얕은 복제
    • clone()을 사용하려면 Cloneable 인터페이스 작성과 구현 필수 public interface Cloneable {}
  • 깊은 복제 원한다면 재정의 필요

📑📌📜✏️

0개의 댓글