언어/Java

[JAVA의정석] Ch7 객체지향 프로그래밍II - 상속, 오버라이딩

닥스훈스 2019. 2. 8. 21:35

1. 상속(Inheritance)

상속이란 기존의 클래스를 재사용하여 새로운 클래스를 작성하는것이다.
키워드 'extends'를 사용하며, 상속해주는 클래스를 '조상 클래스'라 하고 상속받는 클래스를 '자손 클래스'라 한다.

class child extends Parent{
//...
}
이때, 자손 클래스는 조상 클래스의 모든 멤버를 상속받으므로 항상 조상클래스보다 같거나 많은 멤버를 가진다. 주의할 점은 다음과 같다.

  • 생성자와 초기화 블럭은 상속되지 않으며, 멤버만 상속된다.
  • 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

조상클래스만 변경해도, 모든 자손 클래스에 자손의 자손 클래스까지 영향을 미치므로 클래스간의 상속관계를 맺어주면 공통적인 부분은 조상 클래스에서 관리하고 자손 클래스는 자신에 정의된 멤버들만 관리하면 되므로 각 클래스의 코드가 적어져서 관리가 쉬워진다.


1.1 상속관계와 포함관계 결정하기

포함관계한 클래스의 멤버변수로 다른 클래스타입의 참조변수를 선언하는 것이다.

Class Point{
int x;
int y;
}

Class Circle{
Point c = new Point();
int r;
}
이때, 상속관계를 맺어줄것인지 포함관계를 맺어줄것인지 결정하는데 혼돈스러울수 있다. 이때는 다음과 같이 문장을 만들어보면 클래스간의 관계를 명확하게 파악할 수 있다.

  • 상속관계: 원(Circle)은 점(Point)이다. - Circle is a Point
  • 포함관계: 원(Circle)은 점(Point)을 포함한다. - Circle has a Point

위와 같이 '~은 ~이다'라는 문장이 성립한다면, 서로 상속관계를 맺어주고 '~은 을 가지고 있다.'는 문장이 성립한다면 포함관계를 맺어주면 된다. 매번 이렇게 딱 떨어지는것은 아니지만, 적어도 클래스간의 관계를 맺어주는데 기본적인 원칙에 대한 감을 잡을 수 있다.


1.2 단일 상속(Single Inheritance)

자바에서는 다른 객체지향언어인 C++과는 다르게 단일 상속만을 허용한다. 이는 하나의 조상클래스만을 가질 수 있기 때문에 클래스간의 관계가 명확해지고 코드를 더욱 신뢰할 수 있게 만들어준다는 점에서 다중 상속보다 유리하다.


1.3 Object클래스-모든 클래스의 조상

Object클래스모든 클래스의 상속계층도의 최상위에 있는 조상클래스이다.
컴파일러는 자동적으로 'extends Object'를 추가하여 모든 클래스가 자동적으로 Object클래스로부터 상속받게 한다.

이처럼 자바의 모든 클래스들은 Object클래스의 멤버들을 상속받기 때문에 Object클래스에 정의된 멤버들을 사용할 수 있다.
예를 들어, toString(), equls(Object o)와 같은 메서드를 따로 정의하지 않고 사용할 수 있었던 이유는 이 메서드들이 모두 Object클래스에 정의된 것들이기 때문이다.






2. 오버라이딩(Overriding)

[참고]override의 사전적 의미는 '~위에 덮어쓰다(overwrite)'이다.
오버라이딩이란 조상클래스로부터 상속받은 메서드의 내용을 변경하는 것을 말한다. 상속 받은 메서드를 그대로 사용하기도 하지만, 자손 클래스 자신에 맞게 변경해야하는 경우 조상의 메서드를 오버라이딩한다.

class Point{
	int x;
	int y;

	String getLocation(){
		return "x:"+x+",y:"+y;
		}
}

class Point3D extends Point{
		int z;

		String getLocation(){ //오버라이딩
			return "x:"+x+",y:"+y+",z:"+z;
		}
}
위 예제에서 Point3D는 세개의 좌표를 문자열로 반환해야 하므로 조상의 메소드를 오버라이딩하여 Point3D클래스 자신에 맞게 z축의 좌표값도 포함하도록 수정하였다.


2.1 오버라이딩의 조건

오버라이딩은 메서드의 내용만을 새로 작성하는 것 이므로 메서드의 선언부는 조상의 것과 완전히 일치해야 한다. 정리하자면 다음과 같다.

자손 클래스에서 오버라이딩 하는 메서드는 조상클래스의 메서드와
  • 이름이 같아야 한다.
  • 매개변수가 같아야 한다.
  • 반환타입이 같아야 한다.
다만, 접근제어자와 예외는 제한된 조건하에서만 다르게 변경할 수 있다. 정리한 조건은 다음과 같다.

조상클래스의 메서드를 자손클래스에서 오버라이딩 할 때

  • 접근 제어자를 조상클래스의 메서드보다 좁은 범위로 변경할 수 없다.
  • 예외는 조상클래스의 메서드보다 많이 선언할 수 없다.
  • 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

[주의] 두번째조건에서 자손클래스의 예외가 Exception일 경우 Exception은 모든 예외의 최소 조상이므로 가장 많은 개수의 예외를 던질 수 있도록 선언한 것이기 때문에 조건을 만족시키지 못하는 잘못된 오버라이딩이다.

[주의] 조상클래스에 정의된 static메서드를 자손클래스에서 똑같은 이름의 static메서드로 오버라이딩하는 것은 각 클래스의 별개의 static메서드로 정의한것뿐 오버라이딩이 아니다. 각 메서드는 클래스 이름으로 구별될 수 있으므로 호출시 '클래스이름.메서드이름()'을 사용한다.
static멤버들은 자신들이 정의된 클래스에 묶여있다고 생각해야 한다.


2.2 오버로딩 vs 오버라이딩

지금까지 오버로딩과 오버라이딩의 차이점에 대해 배웠다. 두 개념은 이름이 비슷하여 혼돈하기 쉽지만 그 차이는 명백하다.

오버로딩(Overloading)    기존에 없는 새로운 메서드를 정의하는 것(new)

오버라이딩(Overriding)    상속받은 메서드의 내용을 변경하는 것(change, modify)



class Parent{
	void parentMethod() {}
}

class Child extends Parent{
	void parentMethod() {} //오버라이딩
	void parentMethod(int i) {} //오버로딩
	
	void childMethod() {}
	void childMethod(int i) {} //오버로딩
	void childMethod() {} //에러. 중복정의 되었음
}

2.4 super

super자손클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
모든 인스턴스메서드에는 자신이 속한 인스턴스의 주소가 지역변수로 저장되는데, 이것이 참조변수인 this와 super의 값이 된다.
super역시 static메서드에서는 사용할 수 없다.

2.5 super()

super()조상클래스의 생성자를 호출하는데 사용된다.
생성자의 첫 줄에서 조상클래스의 생성자를 호출해야한다.