Java

오버라이딩에서 유의해야 하는점(반환타입과 예외의 상관관계) / SCJP 247번 문제

bang2001 2013. 7. 25. 17:11

오버라이딩시 우리가 일반적으로 알고 있는 규칙은 다음과 같다.


-------------------------------------------------------------------------------------------

1. 접근지정자는 부모객체의 메소드보다 같거나 혹은 접근범위가 더 큰 접근지정자를 
   사용해야 한다.

2. 반환타입은 같아야 한다.

3. 메소드명은 같아야 한다.

4. 매개변수는 같아야 한다.

-------------------------------------------------------------------------------------------

하지만 반드시 위의 규칙에 따르는것은 아니다. 물론 위의 규칙이 기본이지만 상황에 따라서 약간씩

다르게 적용이 된다.

다음 예제를 보자.

------------------------------------------------------------------------------------------

class SuperClass
{
public Object method()
{
return null;
}
}

class SubClass extends SuperClass 
{
@Override
public String method()
{
return null;
}
}

------------------------------------------------------------------------------------------

위의 코드를 보면 반환타입이 틀렸음에도 불구하고 컴파일이 이상없이 완료되고, 실행도

 정상적으로 작동한다. 자세히 살펴보면 반환타입끼리만 놓고 비교하였을때 이 반환타입끼리의

관계는 상속관계인 것을 알 수 있다. 위의 코드에서 정상적으로 작동할 수 있는것은 이 반환타입이

상속관계를 가지고 있고, 위와같은 코드로 되어있을 때 오버라이딩 하면서 객체지향의 특성에 따라서

자식객체에서 오버라이딩 한 메소드의 반환타입이 부모객체에 존재하는 메소드의 반환타입과

묵시적 형변환이 이루어 지기 때문에 이처럼 자식객체자료형으로 반환타입을 지정하면

반드시 반화타입이 같지 않아도 오버라이딩이 되는것이다.

즉 다시말하면 반대의 경우(자식객체자료형으로 반환하는 메소드를 오버라이딩 하면서 부모객체

자료형으로 반환하는 메소드를 만들때)에는 이러한 묵시적 형변환이 이루어 질 수 없으므로, 

오버라이딩을 할 수 없게 된다. 또한 반환타입에 강제적 형변환은 적용이 안되지 않는다.

그 이유는  반환되는 객체에 대해서 메소드를 정의할 때 반환되는 객체의 실체를 알 수 없기 때문이다.

사용하는 사용자에 대해서 어떻게 사용하느냐에 따라서 객체의 실체가 달라질 수 있기 때문이다. 

그리고 이와같은 사실은 오버라이딩할 때 예외를 throws했을 때에도 적용이 된다.

다음 예제를 살펴보자.

-------------------------------------------------------------------------------------------

class 
{
public void process() throws ArithmeticException
}
}
class extends A
{
@Override
public void process() throws Exception
{
}

public static void main(String[] args)
{
}
}

------------------------------------------------------------------------------------------

위와 같은 경우에도 컴파일 에러가 나게 된다. 자식객체(예외)로 throws해주고,

부모타입의 객체(예외)로 throws해주었기 때문이다.

하지만 예외의 경우에는 위의 반환타입과 약간 다른점이 있다. 

그것은 예외를 지정하지 않았을 경우에 자식객체에서 오버라이딩한 메소드에 throws한 경우와

그 반대의 경우를 들 수 있다. 다음 아래 코드를 예시로 들 수 있다.

------------------------------------------------------------------------------------------

class 
{
public void process() 
}
}
class extends A
{
@Override
public void process() throws Exception
{
}

public static void main(String[] args)
{
}
}

-----------------------------------------------------------------------------------------

위와 같은경우에 컴파일 에러가 나게 된다. 컴파일 에러 메세지의 내용을 참고하면 

대략 "부모객체의 메소드는 예외를 throws를 하지 않았는데 자식객체의 메소드는 예외를 

throws 했다"는 메시지가 뜨게 된다. 이러한 경우에 컴파일 에러가 나고,

그 반대인 경우에는 에러가 나지 않는다. 아래 예시를 보자.

------------------------------------------------------------------------------------------

class 
{
public void process() throws Exception
}
}
class extends A
{
@Override
public void process() 
{
}

public static void main(String[] args)
{
}
}

------------------------------------------------------------------------------------------

위의 경우에는 컴파일 에러가 나지 않는다. 이렇게 되는 이유를 생각해보면 (확실한것은 아니다. 추측이다.)

하나의 가정이 나오게 된다. 예외를 throws하지 않은.. 즉 아무것도 throws하지 않은 상태가 

어떤 예외보다도 저 작은 범위라는 것이다. 즉 가장 낮은 자식클래스로 보고있다는 점이다.

하지만 이부분에 대해서는 어디까지나 나의 개인적인 생각일 뿐 반드시 옳다고 확정하기는 힘들다.

하지만 이러한 이유는 둘쨰치더라도 지금까지 설명하면서 예시로 들은것들은 모두 결과가 확실하므로

일단 이렇게 알아두는것이 좋을것 같다.