본문 바로가기
Java

점프 투 자바 - 객체 지향 프로그래밍 5-5 상속

by 햣둘 2024. 10. 15.

1. 상속

2. 자식 클래스의 기능 확장하기

3. IS-A 관계란?

4. 메서드 오버라이딩

5. 메서드 오버로딩

6. 다중 상속이란?

 

1. 상속

상속 (Inheritance) : 자식 클래스가 부모 클래스의 기능을 그대로 물려받을 수 있도록 하는 것

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {  // Animal 클래스를 상속한다.
}

public class Sample {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("poppy");
        System.out.println(dog.name);
    }
}

 

예제 실행 결과 : poppy

 

클래스 상속을 위해서는 extends 라는 키워드를 사용한다.

Dog 클래스에 객체 변수인 name과 메서드인 setName을 만들지 않았지만 

Animal 클래스를 상속했기 때문에 그대로 사용이 가능하다.

 

2. 자식 클래스의 기능 확장하기

이번에는 Dog 클래스에 sleep 메서드를 추가해 보자

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name+" zzz");
    }
}

public class Sample {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("poppy");
        System.out.println(dog.name);
        dog.sleep();
    }
}

예제 실행 결과 :

poppy

poppy zzz

 

* 보통 부모 클래스를 상속받은 자식 클래스는 부모 클래스의 기능에 더하여 좀 더 많은 기능을 갖도록 작성할 수 있다.

 

3. IS-A 관계란?

Dog 클래스는 Animal 클래스를 상속했다.즉, Dog는 Animal의 하위 개념이라고 할 수 있다.이런 경우 Dog는 Animal에 포함되기 때문에 '개(Dog)는 동물(Animal)이다'라고 표현할 수 있다.

 

자바는 이러한 관계를 IS-A 관계라고 표현한다.이렇게 IS-A(상속 관계)에 있을 때 자식 클래스의 객체는 부모 클래스의 자료형인 것처럼 사용할 수 있다.

Animal dog = new Dog();  // Dog is a Animal

 

[주의할 점]

Dog 객체를 Animal 자료형으로 사용할 경우에는 Dog 클래스에만 존재하는 sleep 메서드를 사용할 수 없다.

이런 경우에는 Animal 클래스에 구현된 setName 메서드만 사용이 가능하다.

 

하지만 이 반대의 경우, 

즉 부모 클래스로 만들어진 객체를 자식 클래스의 자료형으로는 사용할 수 없다.

그러므로 아래의 코드는 컴파일 오류가 발생한다.

Dog dog = new Animal(); // 컴파일 오류

 

 

이 부분을 좀 더 개념적으로 살펴보자.

이 코드를 읽어보면 '개(dog)로 만든 객체는 동물(Animal) 자료형이다' 라고 해석할 수 있다.

Animal dog = new Dog();  // Dog is a Animal (O)

 

또 다음의 코드를 보자.

역시 개념적으로 읽어보면 '동물(Animal)로 만든 객체는 개(Dog) 자료형이다'로 해석할 수 있다.

하지만 Animal로 만든 객체는 Dog 말고도 Tiger, Lion 등의 자료형도 될 수 있다.

즉, 개념적으로 살펴보아도 아래의 코드는 성립할 수 없다는 것을 알 수 있다.

Dog dog = new Animal();  // Animal is a Dog (X)

 

< Object 클래스란? >

자바에서 만드는 모든 클래스는 Object 클래스를 상속받는다.

사실 우리가 만든 Animal 클래스는 아래 코드와 기능적으로 완전히 동일하다.

하지만 굳이 아래 코드처럼 Object 클래스를 상속하도록 코딩하지 않아도

자바에서 만들어지는 모든 클래스는 Object 클래스를 자동으로 상속받게끔 되어있다.

class Animal extends Object {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

 

따라서 자바에서 만드는 모든 객체는 Object 자료형으로 사용할 수 있다.

다시 말해, 아래와 같이 코딩하는 것이 가능하다.

Object animal = new Animal();  // Animal is a Object
Object dog = new Dog();  // Dog is a Object

 

4. 메서드 오버라이딩

이번에는 Dog 클래스를 좀 더 구체화시키는 HouseDog 클래스를 만들어 보자.

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name+" zzz");
    }
}

class HouseDog extends Dog {
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz 출력
    }
}

출력 결과 : happy zzz

 

HouseDog 클래스로 만들어진 객체들을 sleep 메서드 호출 시 'happy zzz'가 아닌 'happy zzz in house'로 출력하고 싶으면 어떻게 해야 할까?다음과 같이 HouseDog 클래스를 수정해보자.

class Animal{
	String name;
    
    void setName(String name){
    	this.name = name;
    }
}

class Dog extends Animal {
	void sleep() {
    	System.out.println(tihs.name + " zzz");
    }
}

class HouseDog extends Dog {
	void sleep() {
    	System.out.println(this.name + " zzz in house");
    }
}

public class Sample {
	public static void main(String[] args) {
    	HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep(); // happy zzz in house 출력
    }
}

출력 결과 : happy zzz in houseDog 클래스에 있는 sleep 메서드를 HouseDog 클래스에 다시 구현하여 이와 같이 원하던 결괏값을 얻을 수 있다.

 

HouseDog 클래스에 Dog 클래스와 동일한 형태(즉, 입출력이 동일)의 sleep 메서드를 구현하면

HouseDog 클래스의 sleep 메서드가 Dog 클래스의 sleep 메서드보다 우선순위를 갖게 되어

HouseDog 클래스의 sleep 메서드가 호출되게 된다.

 

이렇게 부모 클래스의 메서드를 자식 클래스가 동일한 형태로 또다시 구현하는 행위

메서드 오버라이딩 (method overriding, 즉 메서드 덮어쓰기)이라고 한다. 

 

5. 메서드 오버로딩

이번에는 HouseDog 클래스에 다음과 같은 메서드를 '추가'해보자

void sleep(int hour) {
	System.out.println(this.name+" zzz in house for " + hour+ "hours");
}

이미 sleep이라는 메서드가 있지만, 동일한 이름의 sleep 메서드를 또 생성할 수 있다.

단, 메서드의 입력 항목이 다를 경우만 가능하다.

새로 만든 sleep 메서드는 입력 항목으로 hour라는 int 자료형이 추가되었다.

 

이렇듯 입력 항목이 다른 경우 동일한 이름의 메서드를 만들 수 있는데 

이를 메서드 오버로딩 (method overloading)이라고 부른다.

 

새로 만든 sleep 메서드를 확인하기 위해 main 메서드를 다음과 같이 변경하고 실행해 보자.

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name + " zzz");
    }
}

class HouseDog extends Dog {
    void sleep() {
        System.out.println(this.name + " zzz in house");
    }

    void sleep(int hour) {
        System.out.println(this.name + " zzz in house for " + hour + " hours");
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz in house 출력
        houseDog.sleep(3);  // happy zzz in house for 3 hours 출력
    }
}

출력 결과 :

happy zzz in house

happy zzz in house for 3 hours

 

6. 다중 상속이란?

다중 상속은 클래스가 동시에 하나 이상의 클래스를 상속받는 것을 뜻한다.

C++,  파이썬 등 많은 언어들이 다중 상속을 지원하지만 

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

 

자바는 다중 상속을 지원하지 않기 때문에 사실 아래의 코드는 실제로 동작할 수 없다.

이해를 위한 것이니 눈으로 보고 이해만 하자.

class A {
    public void msg() {
        System.out.println("A message");
    }
}

class B {
    public void msg() {
        System.out.println("B message");
    }
}

class C extends A, B {
    public void static main(String[] args) {
        C test = new C();
        test.msg();
    }
}

자바가 다중 상속을 지원한다고 가정하고 C 클래스가 A, B라는 클래스를 동시에 상속(extends A, B)하도록 했다.

main 메서드에서 test.msg(); 를 실행할 때 A 클래스의 msg 메서드를 실행해야 할까?

아니면 B 클래스의 msg 메서드를 실행해야 할까?

 

다중 상속을 지원하게 되면 이렇듯 애매모호한 부분이 생기게 된다. 

자바는 이러한 불명확한 부분을 애초에 없앤 언어이다.

 

* 파이썬과 같이 다중 상속을 지원하는 언어들은

이렇게 동일한 메서드를 상속받는 경우 우선순위를 정하는 규칙이 있다.