[java] 재정의 메서드가 재정의 된 메서드보다 더 광범위한 예외를 throw 할 수없는 이유는 무엇입니까?

Kathe sierra의 SCJP 6 책을 살펴보고 재정의 된 메서드에서 예외를 던지는이 설명을 발견했습니다. 나는 그것을 이해하지 못했다. 아무도 나에게 설명 할 수 있습니까?

재정의 메서드는 재정의 된 메서드에 의해 선언 된 것보다 새롭거나 광범위한 검사 된 예외를 throw해서는 안됩니다. 예를 들어 FileNotFoundException을 선언하는 메서드는 FileNotFoundException의 하위 클래스가 아닌 한 SQLException, Exception 또는 기타 비 런타임 예외를 선언하는 메서드로 재정의 할 수 없습니다.



답변

즉, 메서드가 주어진 예외를 throw하도록 선언하면 하위 클래스의 재정의 메서드는 해당 예외 또는 해당 하위 클래스 만 throw하도록 선언 할 수 있습니다. 예를 들면 :

class A {
   public void foo() throws IOException {..}
}

class B extends A {
   @Override
   public void foo() throws SocketException {..} // allowed

   @Override
   public void foo() throws SQLException {..} // NOT allowed
}

SocketException extends IOException,하지만 SQLException그렇지 않습니다.

이것은 다형성 때문입니다.

A a = new B();
try {
    a.foo();
} catch (IOException ex) {
    // forced to catch this by the compiler
}

경우 B던져하기로 한 SQLException다음, 컴파일러는 당신의 인스턴스를 참조하고 있기 때문에 당신이 그것을 잡으려고 강요하지 수 B는 슈퍼 클래스 – A. 반면에의 모든 하위 클래스 IOException는 다음을 처리하는 절 (catch 또는 throws)에 의해 처리됩니다.IOException

수퍼 클래스로 객체를 참조 할 수 있어야하는 규칙은 Liskov Substitution Principle입니다.

확인되지 않은 예외는 어디에서나 throw 될 수 있으므로이 규칙이 적용되지 않습니다. 원할 경우 문서 형식으로 throws 절에 확인되지 않은 예외를 추가 할 수 있지만 컴파일러는 이에 대해 어떤 것도 강제하지 않습니다.


답변

재정의 메서드는 재정의 된 메서드가 예외를 선언하는지 여부에 관계없이 확인되지 않은 (런타임) 예외를 throw 할 수 있습니다.

예:

class Super {
    public void test() {
        System.out.println("Super.test()");
    }
}

class Sub extends Super {
    @Override
    public void test() throws IndexOutOfBoundsException {
        // Method can throw any Unchecked Exception
        System.out.println("Sub.test()");
    }
}

class Sub2 extends Sub {
    @Override
    public void test() throws ArrayIndexOutOfBoundsException {
        // Any Unchecked Exception
        System.out.println("Sub2.test()");
    }
}

class Sub3 extends Sub2 {
    @Override
    public void test() {
        // Any Unchecked Exception or no exception
        System.out.println("Sub3.test()");
    }
}

class Sub4 extends Sub2 {
    @Override
    public void test() throws AssertionError {
        // Unchecked Exception IS-A RuntimeException or IS-A Error
        System.out.println("Sub4.test()");
    }
}


답변

제 생각에는 Java 구문 설계의 실패입니다. 다형성은 예외 처리의 사용을 제한해서는 안됩니다. 실제로 다른 컴퓨터 언어는이를 수행하지 않습니다 (C #).

더욱이 메서드는 더 특수화 된 하위 클래스에서 재정의되므로 더 복잡하고 이러한 이유로 새 예외를 throw 할 가능성이 더 높습니다.


답변

재정의 메서드가 여기에 아무것도 던질 수 없다는 사실을 알려주는 대답이 없기 때문에 여기에이 대답을 제공합니다 . 재정의 메서드가 던질 수 있는 내용은 다음과 같습니다.

1) 동일한 예외 발생

public static class A 
{
    public void m1()
       throws IOException
    {
        System.out.println("A m1");
    }

}

public static class B 
    extends A
{
    @Override
    public void m1()
        throws IOException
    {
        System.out.println("B m1");
    }
}

2) 재정의 된 메서드의 throw 된 예외의 하위 클래스를 throw합니다.

public static class A 
{
    public void m2()
       throws Exception
    {
        System.out.println("A m2");
    }

}

public static class B 
    extends A
{
    @Override
    public void m2()
        throws IOException
    {
        System.out.println("B m2");
    }
}

3) 아무것도 던지지 않습니다.

public static class A 
{   
    public void m3()
       throws IOException
    {
        System.out.println("A m3");
    }
}

public static class B 
    extends A
{   
    @Override
    public void m3()
        //throws NOTHING
    {
        System.out.println("B m3");
    }
}

4) throws에서 RuntimeExceptions가 필요하지 않습니다.

throws에 RuntimeExceptions가 있거나 없을 수 있으며 컴파일러는 그것에 대해 불평하지 않습니다. RuntimeExceptions는 확인 된 예외가 아닙니다. 잡히지 않은 경우 확인 된 예외 만 throw에 나타나야합니다.


답변

이를 설명하기 위해 다음을 고려하십시오.

public interface FileOperation {
  void perform(File file) throws FileNotFoundException;
}

public class OpenOnly implements FileOperation {
  void perform(File file) throws FileNotFoundException {
    FileReader r = new FileReader(file);
  }
}

그런 다음 다음과 같이 작성한다고 가정합니다.

public class OpenClose implements FileOperation {
  void perform(File file) throws FileNotFoundException {
    FileReader r = new FileReader(file);
    r.close();
  }
}

r.close ()가 FileNotFoundException보다 광범위한 IOException을 발생시키기 때문에 컴파일 오류가 발생합니다.

이 문제를 해결하려면 다음을 작성하십시오.

public class OpenClose implements FileOperation {
  void perform(File file) throws IOException {
    FileReader r = new FileReader(file);
    r.close();
  }
}

perform (…) 오퍼레이션을 구현하고 있지만 인터페이스의 메소드 정의에 포함되지 않은 예외가 발생하므로 다른 컴파일 오류가 발생합니다.

이것이 왜 중요한가요? 인터페이스 소비자는 다음을 가질 수 있습니다.

FileOperation op = ...;
try {
  op.perform(file);
}
catch (FileNotFoundException x) {
  log(...);
}

IOException이 발생하도록 허용 된 경우 클라이언트의 코드가 더 이상 정확하지 않습니다.

확인되지 않은 예외를 사용하는 경우 이러한 종류의 문제를 피할 수 있습니다. (나는 당신이 할 것인지 말 것인지를 제안하는 것이 아닙니다. 그것은 철학적 문제입니다)


답변

인터뷰 질문을하겠습니다. 슈퍼 클래스에서 NullPointerException을 발생시키는 메서드가 있습니다. RuntimeException을 발생시키는 메소드로 재정의 할 수 있습니까?

이 질문에 답하려면 Unchecked 및 Checked 예외가 무엇인지 알려주십시오.

  1. 확인 된 예외는 기본 try-catch-finally 예외 처리에 설명 된대로 명시 적으로 포착되거나 전파되어야합니다. 확인되지 않은 예외에는이 요구 사항이 없습니다. 그들은 잡히거나 던져 질 필요가 없습니다.

  2. Java에서 확인 된 예외는 java.lang.Exception 클래스를 확장합니다. 확인되지 않은 예외는 java.lang.RuntimeException을 확장합니다.

공용 클래스 NullPointerException은 RuntimeException을 확장합니다.

확인되지 않은 예외는 java.lang.RuntimeException을 확장합니다. 이것이 NullPointerException이 Uncheked 예외 인 이유입니다.

예를 들어 보겠습니다. 예 1 :

    public class Parent {
       public void name()  throws NullPointerException {
           System.out.println(" this is parent");
       }
}

public class Child  extends Parent{
     public  void name() throws RuntimeException{
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();
        parent.name();// output => child
    }
}

프로그램이 성공적으로 컴파일됩니다. 예 2 :

    public class Parent {
       public void name()  throws RuntimeException {
           System.out.println(" this is parent");
       }
}

public class Child  extends Parent{
     public  void name() throws  NullPointerException {
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();
        parent.name();// output => child
    }
}

프로그램도 성공적으로 컴파일됩니다. 따라서 확인되지 않은 예외의 경우 아무 일도 일어나지 않음이 분명합니다. 이제 Checked 예외의 경우 어떤 일이 발생하는지 살펴 보겠습니다. 예제 3 : 기본 클래스와 자식 클래스가 모두 확인 된 예외를 throw하는 경우

    public class Parent {
       public void name()  throws IOException {
           System.out.println(" this is parent");
       }
}
public class Child  extends Parent{
     public  void name() throws IOException{
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();

        try {
            parent.name();// output=> child
        }catch( Exception e) {
            System.out.println(e);
        }

    }
}

프로그램이 성공적으로 컴파일됩니다. 예제 4 : 기본 클래스의 동일한 메서드와 비교하여 자식 클래스 메서드에서 테두리 확인 예외가 발생하는 경우.

import java.io.IOException;

public class Parent {
       public void name()  throws IOException {
           System.out.println(" this is parent");
       }
}
public class Child  extends Parent{
     public  void name() throws Exception{ // broader exception
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();

        try {
            parent.name();//output=> Compilation failure
        }catch( Exception e) {
            System.out.println(e);
        }

    }
}

프로그램이 컴파일되지 않습니다. 따라서 Checked 예외를 사용할 때주의해야합니다.


답변

메서드 M1이 E1을 던지는 슈퍼 클래스 A와 메서드 M2가 M1을 재정의하는 A에서 파생 된 클래스 B가 있다고 가정합니다. M2는 E1과 다르거 나 덜 전문화 된 것을 던질 수 없습니다.

다형성 때문에 클래스 A를 사용하는 클라이언트는 B를 A 인 것처럼 취급 할 수 있어야합니다. Inharitance ===> Is-a (B is-a A). 클래스 A를 다루는이 코드가 예외 E1을 처리하고 있었다면, M1이 선언 한대로이 체크 된 예외를 던졌지 만 다른 유형의 예외가 던졌습니다. M1이 IOException을 던지는 경우 M2는 IOException이므로 FileNotFoundException을 던질 수 있습니다. A의 클라이언트는 문제없이 이것을 처리 할 수 ​​있습니다. 던져진 예외가 더 넓다면, A의 클라이언트는 이것에 대해 알 기회가 없으므로 그것을 잡을 기회가 없을 것입니다.