JAVA와 같은 객체 지향 언어에서는 이미 존재하는 클래스를 확장하여 새로운 클래스를 만들 수 있습니다.

자식 클래스(하위 클래스)부모 클래스(상위 클래스)를 선택하여 그 부모의 멤버 변수나 함수들을 물려받아 확장할 수 있습니다. 이를 객체 지향 언어의 특성 중에 하나인 상속(Inheritance)이라고 합니다.

이와 같은 상속을 사용하는 이유는 재사용성을 높일 수 있고 중복된 코드를 줄일 수 있습니다. 또한, 개발자가 대규모 프로젝트를 위해 개발하는데 유용하게 사용할 수 있는 기능입니다.

그러면 하위 클래스가 상위 클래스를 어떻게 상속받아 사용할까요?
바로 아래와 같이 일반적인 클래스 정의 형태에 지정어 extends를 사용하여 상위 클래스 이름을 명시해줍니다.

class SubClassName extends SuperClassName {
	...
}

하위 클래스는 상위 클래스의 모든 멤버 변수와 메서드들을 상속받게 됩니다. 또한, 새로운 멤버 변수와 메서드를 추가할 수 있으며, 상위 클래스에 정의된 메서드를 재정의할 수 있습니다.

주의할 점은 extends 키워드 뒤에는 단 하나의 클래스만 올 수 있습니다. 즉, 단일 상속만을 지원합니다.

그럼 이 상속을 하는 경우는 어떤 경우가 있을까요?

일단, 상위 클래스는 상속받을 하위 클래스보다 더 추상적이고 일반적인 개념과 기능을 가집니다. 하위 클래스는 상위 클래스보다 더 구체적인 개념과 기능을 가질 때, 상속받습니다.

즉, 상위 클래스와 하위 클래스는 서로 성질이 같지만 존재하는 클래스보다 내가 새로 만들 클래스는 좀 더 기능이 많고 구체적이였으면 좋겠다면 상속을 받습니다.

괜찮은 클래스의 좋은 기능이 있다고 해서 상속받지는 않습니다.

중요한 점은 하위 클래스를 생성하게 되면 상위 클래스가 먼저 생성하게 됩니다.
그래서 만약에 new SubClassName()를 호출하면 SuperClassName()이 먼저 호출되게 됩니다.
즉, 상속 받은 하위 클래스의 생성자에서는 반드시 상위 클래스의 생성자를 호출합니다.

super()

class SuperClass {
	SuperClass() {
    	System.out.println("SuperClass Constructor...");
    }
}
class SubClass extends SuperClass {
	SubClass() {
    	System.out.println("SubClass Constructor...");
    }
}

public class ConstructorTest {
	public static void main(String[] args) {
    	SubClass subClass = new SubClass();
        System.out.println("main...");
    }
}

결과

SuperClass Constructor...
SubClass Constructor...
main...

위와 같은 코드를 보면 아까 설명했던 것처럼 상위 클래스의 생성자를 호출 후 하위 클래스의 생성자를 호출하는 것을 확인할 수 있습니다.

여기서 하위 클래스의 생성자에서 상위 클래스의 생성자를 super()를 사용하여 명시적으로 호출할 수 있습니다. 또한, 매개변수를 갖는 super()는 상위 클래스의 중복된 생성자를 구별하여 호출하는 역할도 합니다.

바로 아래의 코드로 확인해 보겠습니다.

class SuperClass {
	int a, b;
	SuperClass() { 
    	a = 1; 
        b = 1; 
     }
    SuperClass(int a, int b) {
    	this.a = a;
        this.b = b;
    }
}

class SubClass extends SuperClass {
	int c;
	SubClass() {
    	c = 1;
    }
    SubClass(int a, int b, int c) {
    	super(a, b);
        this.c = c;
    }
}

public class ConstructorTest {
	public static void main(String[] args) {
    	SubClass subClass1 = new SubClass();
        SubClass subClass2 = new SubClass(1, 2, 3);
        System.out.println("a = "+subclass1.a + " b = "+subClass1.b +" c = "+subClass1.c);
        System.out.println("a = "+subclass2.a + " b = "+subClass2.b +" c = "+subClass2.c);
    }
}

결과

a = 1 b = 1 c = 1
a = 1 b = 2 c = 3

객체 subClass1에 대한 생성자에서는 컴파일러에 의해 상위 클래스의 생성자가 자동으로 호출되어 모든 멤버 변수들을 1로 배정하였고, subClass2 객체에 대한 생성자에서는 명시적으로 super(a, b)를 호출하여 각 멤버 변수들을 지정된 값으로 초기화하였습니다.

형변환

형변환이라고 부르지만 어떻게 보면 객체간의 대입이라고도 부를 수 있다고 생각합니다.

형변환은 아래와 같이 상위 클래스로 변수를 선언하고 하위 클래스의 생성자로 인스턴스를 생성하는 것을 말합니다.

SuperClass obj = new SubClass();

하위 클래스는 상위 클래스에 대한 부분을 다 내포하고 있기 때문에 하위 클래스가 상위 클래스로 대입이 된다고 볼 수 있습니다.

여기에 만약, 하위 클래스 객체를 new 연산자를 통해 생성하고 메서드를 호출하는데, 그 메서드의 매개변수 타입이 상위 클래스의 타입인 경우 하위 클래스 객체가 대입될 수 있습니다. 왜냐하면 하위 클래스지만 상위 클래스로 형변환(업캐스팅)이 될 수 있기 때문입니다.

추가적으로 하위 클래스는 상위 클래스의 타입을 내포하고 있기 때문에 상위 클래스로의 묵시적 형 변환이 가능합니다.

하지만 중요한 점은 상위 클래스 객체를 하위 클래스 객체로 형변환하는 것은 불가능합니다.

또한 여기서 중요한 점은 만약 SuperClass obj = new SubClass();를 통해 obj 객체를 생성했다는 가정하에, SubClass() 생성자에 의해 SubClass 클래스의 모든 멤버 변수에 대한 메모리는 생성되었지만 변수의 타입이 SuperClass 이므로 실제 접근 가능한 변수나 메서드는 SuperClass의 변수와 메서드입니다.

지금까지 JAVA의 특성 중 하나인 상속에 대해서 간단히 알아봤습니다.

profile
꾸준함으로 성장하는 개발자 지망생

0개의 댓글