상속

2020-12-26
  • Java

6주차

상속

재사용 + 확장

정의

클래스들은 다른 클래스로부터 파생될 수 있고, 따라서 해당 클래스에서 필드와 메서드를 상속받는다. 다른 클래스로부터 파생된 클래스는 하위 클래스(파생 클래스, 확장 클래스, 자식 클래스)라고 불린다. 서브 클래스를 파생한 클래스는 슈퍼 클래스(부모 클래스)라고 한다.

✔️ 상속관계에서 반드시 만족해야 할 문장

  • 하위클래스는 상위클래스이다
  • 하위 클래스 is a kind of 상위 클래스 관계

특징

단일상속

부모클래스가 없는 Object 클래스를 제외하고, 모든 클래스는 단 하나의 직접적인 부모 클래스를 갖는다. 명시적인 부모 클래스가 없다면, 모든 클래스는 암묵적으로 Object의 자식 클래스이다.

최상위 클래스인 Object

https://docs.oracle.com/javase/tutorial/figures/java/classes-object.gif

출처 - Java docs

클래스는 궁극적으로 최상위 클래스인 Object 클래스로부터 계속적으로 파생된 것이다. 이러한 클래스들은 상속 체인의 모든 클래스로부터 다시 Object로 확장된 것으로 간주된다.

한편 java.lang 패키지에 정의된 Object 클래스는 모든 클래스들에게 공통적인 동작을 정의하고 구현한다. 자바 플랫폼에서는 많은 클래스들이 직접적으로 Object 클래스로부터 파생되고, 또 다른 클래스들이 이러한 클래스들로부터 상속되면서 클래스들의 계층을 생성한다.

계층 가장 위에 있는 Object는 모든 클래스들의 가장 일반적인 클래스이다. 계층 아래로 갈 수록 더욱 행동들이 구체화된다.

⭐상속의 장점 - 재사용

기존 클래스의 필드와 메서드를 직접 작성(and debug)하지 않아도 재사용할 수 있다.

상속에 대한 개념은 간단하지만 강력하다. 새 클래스를 만들고자 할 때, 이미 원하는 코드가 포함된 클래스가 존재한다면, 그 클래스에서 새로운 클래스를 파생할 수 있다.

생성자는 상속이 될까? 🤔

정답은 NO. 하위 클래스는 상위 클래스로부터 모든 멤버(필드, 메서드, 중첩 클래스)생성자는 멤버가 아니므로 하위 클래스에서 상속되지 않는다. 하지만 상위 클래스의 생성자는 하위 클래서에서 호출할 수 있다!

하위 클래스

하위 클래스는 어떤 패키지에 속해있든 상관없이, 상위 클래스의 모든 public, protected 멤버들을 상속받는다. 만약 하위 클래스가 상위 클래스와 동일한 패키지에 있는 경우, 상위 클래스의 package-private 멤버들 역시 사용할 수 있다. 상속된 맴버를 그대로 사용하거나, 대체하거나, 숨기거나, 새로운 멤버로 보완할 수 있다.

접근 제어자

특징

  • private 멤버는 상속받지 못한다. 오직 public, protected 멤버들만 상속 가능하다.
  • 상속된 필드는 다른 필드와 마찬가지로 직접 사용할 수 있다.
  • 권장하지는 않지만, 상위 클래스에 있는 것과 동일한 이름으로 하위 클라스에 필드를 선언하여 숨길 수 있다.
  • 상위 클래스에 없는 하위 클래스의 새 필드를 선언할 수 있다.
  • 상속된 메서드는 그대로 직접 사용할 수 있다.
  • 오버라이딩이 가능하다 - 상위 클래스의 메서드와 동일한 이름을 가진 메서드를 하위 클래스에서 재정의할 수 있다.
  • 상위 클래스에 있는 것과 동일한 이름을 가진 새로운 static 메서드를 하위 클래스에서 작성할 수 있고, 이를 숨길 수 있다.
  • 상위 클래스에 없는 새 메서드를 하위 클래스에서 선언할 수 있다.
  • 암시적으로 또는 명시적인 super 키워드를 사용하여, 상위 클래스의 생성자를 호출하는 하위 클래스 생성자를 작성할 수 있다.

하위 클래스에서 상위 클래스에서의 private 멤버 사용하는 법?

  • ⭐ 하위 클래스는 부모 클래스의 private 멤버를 상속하지 않는다. 하지만 만약 상위 클래스가 private 필드에 접근할 수 있는 public or protected 메서드가 있다면, 하위 클래스에서도 사용할 수 있다.
  • 중첩 클래스는 필드와 메서드 모두를 포함하는 클래스의 모든 private 멤버에 엑세스할 수 있다. 따라서 하위 클래스에서 상속된 public 또는 protected 중첩 클래스는 상위 클래스의 모든 private 멤버에 간접적으로 엑세스할 수 있다.

super와 super()

super

super 키워드를 사용하여 부모 클래스의 멤버에 접근할 수 있다.

class A {
	int a = 1;
}
class B extends A {
	int a = 2;
	void display() {
		System.out.println(a);
		System.out.println(this.a);
		System.out.println(super.a);
	}
}

super()

부모 클래스의 생성자를 호출할 때 사용한다.

Casting

상위 클래스가 Animal이고, 하위 클래스가 Dog라고 가정해보자. Dog 클래스는 Animal로부터 상속되었고, Animal은 Object로부터 상속되었으므로, DogAnimal이자 Object이며, Animal 또는 Object 객체가 호출되는 모든 곳에서 사용할 수 있다. → 하지만 반대는 성립하지 않는다!

암시적 형변환

Casting은 상속 및 구현에 의해 허용되는 객체 사이에서, 다른 타입 대신 한 유형의 객체 사용을 보여준다.

Object obj = new Dog();

obj는 Object 이면서 Dog이다.

명시적 형변환

Dog dog = (Dog)obj;

이러한 형변환은 컴파일러가 obj가 Dog라는 것을 안전하게 판단할 수 있도록, obj에 Dog가 할당되었는지를 확인하는 런타임 검사를 삽입한다. 만약 obj가 런타임에 Dog가 아니라면 예외가 발생한다.

instanceof

instanceof 연산자를 사용해서, 특정 개객체에 대한 논리적인 테스트를 할 수 있다. 이 테스트를 통해 runtime에 부적절한 형변환에 대한 런타임 에러를 막을 수 있다.

if(obj instanceof dog) {
	Dog dog = (Dog)obj;
}

클래스 vs 인터페이스

  • field 유, 무

클래스는 필드를 가질 수 있지만 인터페이스는 필드를 가질 수 없다. 또한 인터페이스로는 수행할 수 없는 클래스를 인스턴트화할 수 있다.

  • 다중상속

자바에서는 다중상속을 지원하지 않는다.

다중상속의 문제점

예를들어 한 클래스가 여러 클래스를 상속한다고 가정하자. 해당 클래스를 인스턴트화하여 객체를 만들면, 그 객체는 클래스의 상위 클래스로부터 필드를 상속할 것이다. 만약 이 객체가 아닌, 또 다른 상속받는 객체에서 상위 클래스의 메서드나 생성자가 동일한 필드를 인스턴트화 하면 무슨일이 발생할까? 어떤 메서드 또는 생성자가 우선할까? 이러한 문제로 인터페이스는 필드를 포함하지 않기 때문에 다중상속의 문제를 해결한다.

구현의 다중상속은 여러 클래스에서 메서드 정의를 상속하는 기능이다. 이러한 유형의 다중상속에서 이름 충돌 및 모호성과 같은 문제가 발생한다. 이러한 유형의 다중 상속을 지원하는 프로그래밍 언어의 컴파일러가 동일한 이름의 메서드를 포함하는 상위 클래스를 만나면 액세스하거나 호출할 멤버 또는 메서드를 결정할 수 없는 경우가 생긴다.

따라서 자바는 다중 상속과 같은 효과를 내는 인터페이스를 지원한다.

Overriding

정의

같은 메서드 이름, 같은 인자, 같은 리턴 타입

상속관계에 있는 클래스의 성격에 맞게 부모 클래스의 함수를 재정의하는 것이다 → 메서드와 이름과 용례가 같음

Covariant return type

오버라이딩 될 때 서브 클래스의 타입으로 교체될 수 있는 것이다.

public class Foo {
    public A foo() {
        return new A();
    }
}
class Bar extends Foo {
    @Override
    public B foo() {
        return new B();
    }
}

class A {}
class B extends A {}

Dynamic Method Dispatch

다이내믹 메서드 디스패치는 런타임 때 오버라이딩된 메서드에 대한 호출을 해결하여 런타임 때 다향성을 구현하도록 하는 메카니즘이다. 오버라이딩된 메서드가 참조에 의해 호출되면, 자바는 참조하는 객체의 타입에 따라 실행할 해당 메서드의 버전을 결정한다.

추상 클래스

정의

추상 클래스는 abstract를 붙여 선언할 수 있고, 추상 메서드를 포함할 수도, 안할 수도 있다. 추상 메서드가 아니라면, 추상 클래스 내에서 메서드 자체는 구현할 수 있다. 하지만 추상 메서드는 반드시 하위 클래스에서 구현을 제공해야한다.

메서드

어떠한 구현도 없이 선언된 메서드이다. ({} 없이 세미콜론이 따라온다)

abstract void moveTo(double dX, double dY);
  • 만약 추상 메서드를 포함하고자 한다면, 클래스는 무조건 추상 클래스로 선언되어야 한다.
public abstract class A {
	abstract void a();
}

특징

  • 추상 클래스는 인스턴트화할 수 없다.
    • 하지만 상속할 수 있다!
  • 추상 메서드를 포함하려면 클래스는 무조건 추상 클래스로 선언되어야 한다.
  • 추상클래스를 상속할 때, 하위 클래스는 보통 상위 클래스에 있는 추상 메서드에 대한 구현을 제공한다.
    • 만약 구현을 하지 않는다면 하위 클래스 역시 추상 클래스로 선언되어야 한다.

default 또는 static으로 선언되지 않은 인터페이스의 메서드는 암시적으로 추상적이므로, abstract modifier와 인터페이스 메서드는 함께 사용되지 않는다. (사용할 수는 있지만 불필요함)

vs Interface

인스턴트화 하지 못한다는 점이나, 메서드의 구현을 할 수 없다는 점에서 추상 클래스와 인터페이스는 유사하다.

하지만 추상 클래스에서는 static, final이 아닌 필드를 선언할 수 있고, public, protected, private 메서드를 정의할 수 있다.

인터페이스에서는, 모든 필드가 자동으로 public, staticfinal이 되고 선언하거나 정의된 모든 메서드는 public이다. 또한 추상 클래스와 관계없이 클래스는 딱 한 개만 상속할 수 있는 것에 반해 인터페이스는 여러번 구현 가능하다.

final

final 클래스

final이 클래스 이름 앞에 사용되면, 클래스를 상속받을 수 없음을 명시적으로 지정하는 것이다. 즉, 상속할 수 없다

final 메서드

메서드 앞에 final이 붙으면, 그 메서드는 오버라이딩 할 수 없다

final 클래스가 아닌, 특정 method만 오버라이딩 할 수 없게 할 때, method 앞에 붙여 사용한다.

  • 부모클래스에서 정의한 method를 자식 클레스가 그대로 쓰게 하고 싶을 때 사용한다.

final 필드

  1. final로 상수 필드를 정의하려면, 선언할 때 초기값을 지정해야한다
  2. 한 번 정의되면 값을 변경할 수 없다

static + final -> 프로그램 전체에서 공유하여 사용가능한 상수

final을 쓰는 이유?

final을 선언하면서, 상수와 같은 개념을 가진다. 즉, 변경할 수 없는 “Read Only”와 같다.

출처

https://blog.naver.com/PostView.nhn?blogId=bluerein_&logNo=221288112925&redirect=Dlog&widgetTypeCall=true&directAccess=false

https://www.geeksforgeeks.org/dynamic-method-dispatch-runtime-polymorphism-java/

https://gmlwjd9405.github.io/2018/08/06/java-final.html

Profile picture

2yeseul

트리플에서 백엔드 개발을 맡고 있습니다. 무한 삽질을 기록합니다. ⚒️