공부/전자컴퓨터공학

Java 상속(Inheritance) 이란? 자바 기초 배우기

AhJustC 2024. 5. 27. 20:51
반응형
상속

자바 언어에서 상속이란 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 문법요소를 의미한다. 두 클래스를 상위 클래스와 하위 클래스로 나누어 상위 클래스의 멤버(필드, 메서드, 내부 클래스)를 하위 클래스와 공유하는 것을 의미한다.이 두 클래스는 서로 상속 관계에 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속 받게 된다. 

위의 그림을 예시로 보자. 총 4개의 클래스가 정의되어 있다. 먼저 트리구조 아래의 3개 클래스를 정리해보자.

  • Programmer 클래스
    속성으로 이름, 나이, 회사 이름이 있고, 기능으로 배우기, 걷기, 먹기, 코딩하기 가 정의되어 있음
  • Dancer 클래스
    속성으로 이름, 나이, 그룹명이 있고, 기능으로 배우기, 걷기, 먹기, 춤추기 가 있음
  • Singer 클래스
    속성으로 이름, 나이, 밴드명이 있고, 기능으로는 배우기, 걷기, 먹기, 노래하기가 있음

세 개의 클래스에 공통적인 속성과 기능이 정의되어 있다는 것을 알 수 있다. 사람이라면 누구나 이름과 나이가 있고, 무언가 배우고 걷고 먹는 기능을 한다. 즉 사람이라는 상위 클래스 에서 프로그래머, 댄서, 가수 클래스가 특정한 속성과 기능을 내려받는 하위 클래스가 된다. 상속을 사용하는 이유는 코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있어 코드의 중복을 제거할 수 있기 때문이다. 또한 상속은 다형적 표현이 가능하다는 장점이 있다.

 

상속 코드 예제
class Person {
    String name;
    int age;

    void learn(){
        System.out.println("공부를 합니다.");
    };
    void walk(){
        System.out.println("걷습니다.");
    };
    void eat(){
        System.out.println("밥을 먹습니다.");
    };
}

class Programmer extends Person { // Person 클래스로부터 상속. extends 키워드 사용
    String companyName;

    void coding(){
        System.out.println("코딩을 합니다.");
    };
}

class Dancer extends Person { // Person 클래스로부터 상속
    String groupName;

    void dancing(){
		    System.out.println("춤을 춥니다.");
		};
}

class Singer extends Person { // Person 클래스로부터 상속
    String bandName;

    void singing(){
		    System.out.println("노래합니다.");
		};
    void playGuitar(){
		    System.out.println("기타를 칩니다.");
		};
}

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

        //Person 객체 생성
        Person p = new Person();
        p.name = "김코딩";
        p.age = 24;
        p.learn();
        p.eat();
        p.walk();
        System.out.println(p.name);

        //Programmer 객체 생성
        Programmer pg = new Programmer();
        pg.name = "박해커";
        pg.age = 26;
        pg.learn(); // Persons 클래스에서 상속받아 사용 가능
        pg.coding(); // Programmer의 개별 기능
        System.out.println(pg.name);

    }
}

//출력값
공부를 합니다.
밥을 먹습니다.
걷습니다.
김코딩
공부를 합니다.
코딩을 합니다.
박해커
반응형

자바에서 상속을 구현하는 방법은 클래스명 다음에 extends 키워드를 사용하여 정의한다. 보는것처럼 Person 클래스로부터 Programmer, Danser, Singer 클래스가 확장되어 Person 클래스에 있는 속성과 기능들을 사용할 수 있는 것을 확인할 수 있다.

 참고로 자바의 객체지향 프로그래밍에서는 단일 상속(Single inheritance)만을 허용한다. 

 

포함 관계

포함(composite)은 상속처럼 클래스를 재사용할 수 있는 방법으로, 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것을 의미한다. 아래의 예시를 통해 확인해보자.

public class Employee {
    int id;
    String name;
    Address address;

    public Employee(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    void showInfo() {
        System.out.println(id + " " + name);
        System.out.println(address.city+ " " + address.country);
    }

    public static void main(String[] args) {
        Address address1 = new Address("서울", "한국");
        Address address2 = new Address("도쿄", "일본");

        Employee e = new Employee(1, "김코딩", address1);
        Employee e2 = new Employee(2, "박해커", address2);

        e.showInfo();
        e2.showInfo();
    }
}

class Address {
    String city, country;

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }
}

// 출력값
1 김코딩
서울 한국
2 박해커
도쿄 일본

위의 예시를 보면 한 회사의 근로자(Employee)를 표현하기 위한 Employee 클래스의 멤버 변수로 근로자가 사는 개략적인 주소를 나타내는 Adderss 클래스가 정의되어 있다. 원래라면 Address 클래스에 포함되어 있는 인스턴스 변수 city와 country를 각각 Employee 클래스의 변수로 정의해야하지만, Address 클래스로 해당 변수들을 묶어준 다음 Employee 클래스 안에 참조변수를 선언하는 방법으로 코드의 중복을 없애고 포함관계로 재사용 하고 있다.

클래스 간의 관계가 ~은~이다(IS-A) 관계라면 상속관계가 적합하고, ~은 ~을 가지고 있다(Has-A) 가 되면 포함 관계가 적합하다.

 

메서드 오버라이딩

메서드 오버라이딩(Method Overriding)은 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의 하는 것을 의미한다. 간단한 예시를 통해 살펴보자.

public class Main {
    public static void main(String[] args) {
        Bike bike = new Bike();
        Car car = new Car();
        MotorBike motorBike = new MotorBike();

        bike.run();
        car.run();
        motorBike.run();
    }
}

class Vehicle {
    void run() {
        System.out.println("Vehicle is running");
    }
}

class Bike extends Vehicle {
    void run() {
        System.out.println("Bike is running");
    }
}

class Car extends Vehicle {
    void run() {
        System.out.println("Car is running");
    }
}

class MotorBike extends Vehicle {
    void run() {
        System.out.println("MotorBike is running");
    }
}

// 출력값
Bike is running
Car is running
MotorBike is running

 

 

 

 

위의 예시에서 vehicle 클래스에 run()메서드가 정의도어 있으며 Bike, Car, Motor Bike 클래스에서 run(() 메서드를 재정의함으로써 Vehicle 클래스의 run()메서드를 오버라이딩 하고 있다. 따라서 Bike, Car, MotorBike 의 인스턴스를 통해 run() 메서드를 호출하면 Vehicle의 run()이 아닌 Bike, Car, MotorBike의 run()이 호출된다. 이처럼 메서드 오버라이딩은 상위 클래스에 정의된 메서드를 하위 클래스에서 메서드의 동작을 하위 클래스에 맞게 변격하고자 할 때 사용한다.

상위 클래스의 메서드를 오버라이딩 하려면 세가지 조건이 필요하다.

  • 메서드의 선언부((메서드 이름, 매개 변수, 변환 타입)가 상위 클래스의 그것과 완전히 일치해야 한다.
  • 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다.
  • 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다.
super 키워드와 super()

super 키워드는 상위 클래스의 객체, super()는 상위 클래스의 생성자를 호출하는 것을 의미한다. 이 둘은 공통적으로 상위 클래스의 존재를 상정하며 상속 관계를 전제로 한다.아래 예시를 통해 super 키워드를 알아보자.

public class Example {
    public static void main(String[] args) {
        SubClass subClassInstance = new SubClass();
        subClassInstance.callNum();
    }
}

class SuperClass {
    int count = 20; // super.count
}

class SubClass extends SuperClass {
    int count = 15; // this.count

    void callNum() {
        System.out.println("count = " + count);
        System.out.println("this.count = " + this.count);
        System.out.println("super.count = " + super.count);
    }
}

// 출력값
count = 15
count = 15
count = 20

위의 예제에서 SubClass는 SuperClass로부터 변수 count를 상속받는데 공교롭게도 자신의 인스턴스 변수 count와 이름이 같아 둘을 구분할 방법이 필요하다. 이럴 경우 두 개의 같은 이름의 변수를 구분하기 위한 방법이 바로 super 키워드이다. 만약 super 키워드를 붙이지 않는다면 자바 컴파일러는 해당 객체는 자신이 속한 인스턴스 객체의 멤버를 먼저 참조한다. 반면 경우에 따라서 상위 클래스의 변수를 참조해야 할 경우가 종종 있는데 그 경우 super 키워드를 사용하면 부모의 객체의 멤버 값을 참고할 수 있다. 즉 상위 클래스의 멤버와 자신의 멤버를 구별하는 데 사용된다는 점을 제외한다면 this와 super는 기본적으로 같은것이라 말할 수 있다.

위의 예시에서 첫 번째 count는 자기에게서 가장 가까운 count 변수인 15를 가리킨다. 두 번째 카운트 또한 자신이 호출된 객체의 인스턴스 변수를 가리키기 때문에 15를 출력한다. 마지막으로 super키워드를 사용하여 호출한 count는 상위 클래스의 변수를 참조하여 숫자 20을 출력한다.

아래 예시를 통해 super()메서드도 함께 살펴보자.

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
    }
}

class Human {
    Human() {
        System.out.println("휴먼 클래스 생성자");
    }
}

class Student extends Human { // Human 클래스로부터 상속
    Student() {
        super(); // Human 클래스의 생성자 호출
        System.out.println("학생 클래스 생성자");
    }
}

// 출력값
휴먼 클래스 생성자
학생 클래스 생성자

Human 클래스를 확장하여 Student 클래스를 생성하고 Student 생성자를 통해 상위 클래스 Human 클래스의 생성자를 호출하고 있다. super()메서드는 생성자 안에서만 사용 가능하며 가장 첫 줄에 와야 한다. super()메서드는 Student 클래스 내부에 호출되고 있고 상위클래스 Human 의 생성자를 호출하고 있다. 그리고 출력값으로 휴면 클래스 생성자와 학생 클래스의 생성자 가 순차적으로 출력된다. super()가 없는 경우에는 컴파일러가 생성자의 첫줄에 자동으로 super()를 삽입한다.

 

Object 클래스

Object 클래스는 자바의 클래스 상속 계층도에서 최상위에 위치한 클래스이다. 따라서 자바의 모든 클래스는 Object 클래스로부터 확장된다는 명제는 참이다. 실제로 자바 컴파일러는 컴파일 과정에서 다른 클래스로부터 아무런 상속을 받지 않는 클레스에 자동으로 extends Object를 추가하여 Object 클래스를 상속받도록 한다.

class ParentEx {  //  컴파일러가 "extends Object" 자동 추가

}

class ChildEx extends ParentEx {

}

ParentEx 클래스를 상속받아 ChildEx 클래스를 만들었을 때 상위클래스 ParentEx는 아무것도 상속하고 있지 않기에 컴파일러는 extends Object를 삽입한다. Object 클래스는 자바 클래스의 상속계층도에 가장 위에 위치하기 때문에 Object 클래스의 멤버들을 자동으로 상속받아 사용할 수 있다. 아래는 Object 클래스의 대표적인 메서드이다.

메서드명 변환타입 주요내용
toString() String 객체 정보를 문자열로 출력
equals(Object obj) boolean 등가 비교 연산(==)과 동일하게 스택 메모리값을 비교
hashCode() int 객체의 위치정보 관련. Hashtable 또는 HashMap에서 동일 객체여부 판단
wait() void 현재 스레드 일시정지
notify() void 일시정지 중인 스레드 재동작
반응형