C++와 Java는 모두 객체 지향 프로그래밍의 핵심 개념인 상속(Inheritance)을 지원합니다. 상속은 기존 코드를 재사용하고 두 객체 사이에 계층적 관계를 형성하는 데 활용되는 강력한 기능입니다. 하지만 두 언어가 상속을 구현하고 처리하는 방식에는 몇 가지 중요한 차이점이 존재합니다. 이 글에서는 C++와 Java의 상속 메커니즘을 비교하며 그 차이점을 자세히 살펴보겠습니다.
1. Java의 모든 클래스는 Object 클래스를 상속받습니다
Java에서는 모든 클래스가 암묵적으로 Object 클래스를 확장(extends)합니다. 따라서 Java의 클래스 계층 구조는 항상 단일 루트를 가진 트리 형태를 이루며, 그 최상위 루트에는 Object 클래스가 위치합니다. 아래의 간단한 코드를 통해 이 사실을 직접 확인할 수 있습니다.
예제 코드
// MyClass.java 파일에 저장
public class MyClass {
MyClass() {
System.out.println("This is constructor of MyClass.");
}
}
// Test.java 파일에 저장
public class Test {
public static void main(String[] args) {
MyClass obj = new MyClass();
System.out.println("obj is an instance of Object: " + (obj instanceof Object));
}
}
실행 결과
This is constructor of MyClass. obj is an instance of Object: true
instanceof 연산자의 결과가 true로 출력되는 것을 보면, 별도로 상속을 선언하지 않아도 모든 객체가 Object 클래스의 인스턴스임을 알 수 있습니다.
2. 조부모 클래스 멤버에 대한 직접 접근
Java에서는 조부모(grandparent) 클래스, 즉 부모의 부모 클래스에 해당하는 멤버에 직접 접근할 수 없습니다. 반면 C++에서는 범위 지정 연산자(::)를 사용해 특정 조상 클래스의 멤버에 명시적으로 접근할 수 있으므로, 이 부분에서 두 언어의 동작 방식이 크게 달라집니다.
3. protected 접근 제어자의 차이
Java의 protected 가시성은 C++와 약간 다르게 동작합니다. Java에서는 기본 클래스(부모 클래스)의 protected 멤버를, 해당 클래스를 상속하지 않았더라도 같은 패키지에 속한 다른 클래스에서 접근할 수 있습니다. 아래 예제에서 상속 관계가 전혀 없는 Test 클래스가 MyClass의 protected 멤버에 접근하는 것을 확인할 수 있습니다.
예제 코드
// MyClass.java 파일에 저장
public class MyClass {
protected int x = 10;
protected int y = 20;
}
// Test.java 파일에 저장
public class Test {
public static void main(String[] args) {
MyClass obj = new MyClass();
System.out.println("x is: " + obj.x + " y is: " + obj.y);
}
}
실행 결과
x is: 10 y is: 20
반면 C++의 protected 멤버는 오직 해당 클래스 자신과 파생 클래스(그리고 friend 클래스)에서만 접근할 수 있으므로, 위와 같은 코드는 컴파일 오류를 발생시킵니다.
4. extends 키워드와 상속 가시성
Java에서는 상속을 위해 반드시 extends 키워드를 사용해야 합니다. 흥미로운 점은 C++에서는 상속 시 public, protected, private으로 상속의 가시성을 지정할 수 있지만, Java에서는 이를 변경할 방법이 없다는 것입니다. 따라서 Java에서는 기본 클래스에서 public 또는 protected로 선언된 멤버가 파생 클래스에서도 동일한 접근 수준을 그대로 유지합니다.
5. 가상 메서드(Virtual Method)
Java의 모든 메서드는 기본적으로 virtual입니다. 즉, 별도의 키워드 없이도 하위 클래스에서 메서드를 오버라이딩하면 다형성(polymorphism)이 자연스럽게 적용됩니다. 반면 C++에서는 오버라이딩을 통해 동적 바인딩을 원할 경우 virtual 키워드를 명시적으로 선언해야 합니다.
6. 다중 상속(Multiple Inheritance)과 인터페이스
C++에서는 하나의 클래스가 여러 클래스를 동시에 상속하는 다중 상속이 허용됩니다. 하지만 Java에서는 다중 상속을 직접 지원하지 않습니다. 이는 여러 부모 클래스로 인해 발생할 수 있는 모호성(ambiguity) 문제, 예를 들어 유명한 '다이아몬드 문제'를 방지하기 위한 언어 설계 결정입니다.
대신 Java는 인터페이스(Interface)를 통해 다중 상속과 유사한 효과를 얻을 수 있습니다. 인터페이스는 순수 추상 클래스에 해당하며, 완전히 구현된 메서드가 없기 때문에 어떤 구현을 따라야 할지에 대한 모호성이 발생하지 않습니다. 하나의 클래스는 여러 인터페이스를 구현(implements)함으로써 다중 상속의 장점을 안전하게 누릴 수 있습니다.
핵심 차이점 요약
| 비교 항목 | C++ | Java |
|---|---|---|
| 최상위 부모 클래스 | 별도의 공통 루트 없음 | 모든 클래스가 Object 상속 |
| 조부모 클래스 멤버 접근 | :: 연산자로 직접 접근 가능 | 직접 접근 불가 |
| protected 범위 | 파생 클래스에서만 접근 가능 | 같은 패키지의 클래스도 접근 가능 |
| 상속 가시성 지정 | public / protected / private 지정 가능 | 변경 불가 (extends만 사용) |
| 가상 메서드 | virtual 키워드 명시 필요 | 기본적으로 모든 메서드가 virtual |
| 다중 상속 | 클래스 다중 상속 지원 | 미지원, 인터페이스로 대체 |
이처럼 C++와 Java는 같은 '상속'이라는 개념을 공유하지만, 언어의 설계 철학에 따라 세부 동작이 상당히 다릅니다. 두 언어의 차이를 정확히 이해하면 프로젝트에 적합한 언어를 선택하고, 더 안전하고 유지보수하기 좋은 객체 지향 코드를 작성하는 데 큰 도움이 될 것입니다.