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


104

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

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


1
도움이 될만한 사이트는 다음과 같습니다. javapractices.com/topic/TopicAction.do?Id=129
Tim Bish

답변:


155

즉, 메서드가 주어진 예외를 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 절에 확인되지 않은 예외를 추가 할 수 있지만 컴파일러는 이에 대해 어떤 것도 강제하지 않습니다.


이것은 인터페이스를 구현하는 동안에도 적용됩니까? 인터페이스 구현이 여전히 "재정의"라고 불리는 지 잘 모르겠습니다.
Muhammad Gelbana 2013

@Override public void foo () {..} 허용되는 것을 알고 있지만이 경우에 대한 설명이 명확하지 않습니다.
nascar

4
@danip 재정의 메서드는 재정의 된 메서드에서 throw 된 예외의 하위 집합을 throw 할 수 있습니다. 빈 세트도 하위 집합입니다. 그것이 @Override public void foo() {...}합법적 인 이유 입니다.
개발자 Marius Žilėnas

@Bozho 그것은 안 하는 방법은 주어진 예외를 던져 선언하는 경우, 서브 클래스에서 재정의하는 방법은 해당 예외 또는 그 서브 클래스를 던져 선언 할 수 있습니다 또는 NO 전혀 throws 절로 선언
라만 Sahasi

그렇다면 현실 세계에서 어떻게 극복할까요? 구현 된 인터페이스에서 메서드를 재정의해야하지만 내 구현에는 throws 감속이 포함되어 있지만 인터페이스에는 그렇지 않습니다. 여기서 표준 절차는 무엇입니까?
AP

22

재정의 메서드는 재정의 된 메서드가 예외를 선언하는지 여부에 관계없이 확인되지 않은 (런타임) 예외를 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()");
    }
}

인터페이스가 하위 클래스가 수행하는 런타임 예외를 선언하지 않을 때 어떻게 오류 또는 경고를 강제합니까? 문서화 목적으로 일관성을 유지하려고합니다. 인터페이스 유형을 찾은 다음 IOException 또는 IllegalArgumentException이 발생하는지 확인하기 위해 구현을 조사하는 것보다 모든 예외에 대해 인터페이스 유형을 확인하고 선택하지 않는 것이 더 쉽습니다.
anon58192932

14

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

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


8

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

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에 나타나야합니다.


6

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

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이 발생하도록 허용 된 경우 클라이언트의 코드가 더 이상 정확하지 않습니다.

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


3

인터뷰 질문을하겠습니다. 슈퍼 클래스에서 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 예외를 사용할 때주의해야합니다.


2

메서드 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의 클라이언트는 이것에 대해 알 기회가 없으므로 그것을 잡을 기회가 없을 것입니다.


Perhac :: 체크 된 예외와 체크 해제 된 예외 모두에 해당합니까? 아니면 다양합니까?
ylnsagar

@ylnsagar 이것은 확인 된 예외에만 해당됩니다. 검사되지 않은 예외 (RuntimeException의 하위 유형)는 '프로그래머 오류'라고도 할 수 있으며 일반적으로 try-catch해서는 안되므로 throws 절에서 선언 할 필요가 없습니다. 확인되지 않은 예외는 모든 코드에서 언제든지 발생할 수 있습니다. 위의 논의는 확인 된 예외에만 관한 것입니다
Peter Perháč

@Perhac :: 예, 맞습니다.하지만 기사를 읽음으로써 제 이해에서. 확인되지 않은 예외에 대해서도 마찬가지입니다. 예를 들어 슈퍼 클래스 메서드가 Null Pointer Exception을 throw하고 메서드를 재정의하는 하위 클래스가 Exception을 throw하는 경우입니다. 여기서 Exception은 Null Pointer Exception의 수퍼 클래스입니다. 그렇다면 컴파일러는 이것을 허용하지 않을 것입니다.
ylnsagar

1

잘 java.lang.Exception은 java.lang.Throwable을 확장합니다. java.io.FileNotFoundException은 java.lang.Exception을 확장합니다. 따라서 메서드가 java.io.FileNotFoundException을 throw하면 재정의 메서드에서 FileNotFoundException보다 더 높은 계층 구조를 throw 할 수 없습니다. 예를 들어 java.lang.Exception을 throw 할 수 없습니다. 그래도 FileNotFoundException의 하위 클래스를 던질 수 있습니다. 그러나 재정의 된 메서드에서 FileNotFoundException을 처리해야합니다. 코드를 노크하고 시도해보세요!

다형성이 슈퍼 클래스에서 재정의 된 메서드를 호출 할 수 있음을 의미하므로 규칙이 있으므로 특이성을 넓혀 원래 throws 선언을 잃지 않습니다.


1

재정의 메서드는 재정의 된 메서드에 의해 선언 된 것보다 새롭거나 광범위한 검사 된 예외를 throw해서는 안됩니다.

예:

class Super {
    public void throwCheckedExceptionMethod() throws IOException {
        FileReader r = new FileReader(new File("aFile.txt"));
        r.close();
    }
}

class Sub extends Super {    
    @Override
    public void throwCheckedExceptionMethod() throws FileNotFoundException {
        // FileNotFoundException extends IOException
        FileReader r = new FileReader(new File("afile.txt"));
        try {
            // close() method throws IOException (that is unhandled)
            r.close();
        } catch (IOException e) {
        }
    }
}

class Sub2 extends Sub {
    @Override
    public void throwCheckedExceptionMethod() {
        // Overriding method can throw no exception
    }
}

1

재정의 메서드는 재정의 된 메서드에 의해 선언 된 것보다 새롭거나 광범위한 검사 된 예외를 throw해서는 안됩니다.

이는 단순히 기존 메서드를 재정의 할 때이 오버로드 된 메서드가 throw하는 예외가 원래 메서드가 throw하는 것과 동일한 예외이거나 해당 하위 클래스 중 하나 여야 함을 의미합니다 .

확인 된 모든 예외가 처리되는지 여부를 확인하는 것은 런타임이 아닌 컴파일 타임에 수행됩니다. 따라서 컴파일 타임 자체에서 Java 컴파일러는 재정의 된 메서드가 throw하는 예외 유형을 확인합니다. 어떤 오버라이드 된 메소드가 실행 될지는 런타임에서만 결정할 수 있기 때문에 어떤 Exception을 잡아야하는지 알 수 없습니다.


class A와 그 하위 클래스가 있다고 가정 해 봅시다 B. Ahas method m1and class Bhas overridden this method (lets call it m2to avoid confusion ..). 이제 할 말은 m1발생 E1하고, m2발생 E2E1의 슈퍼 클래스. 이제 다음 코드를 작성합니다.

A myAObj = new B();
myAObj.m1();

참고 m1아무것도하지만를 호출하지 않습니다는 m2(다시, 메소드 서명 때문에 혼동을하지 않는 오버로드 된 메서드에서 동일 m1하고 m2둘 다 동일한 서명이 ... ... 그들은이 예에서 차별화하는 데 불과하다). 그러나 컴파일 타임에 자바 컴파일러가 수행하는 모든 작업은 참조 유형 ( A이 경우 Class )이 메소드가 있는지 확인하고 프로그래머가 처리 할 것으로 예상하는 것입니다. 따라서 분명히 던지거나 잡을 것 E1입니다. 이제 런타임에 오버로드 된 메서드가 의 슈퍼 클래스 인을 throw E2하면 E1... 음, 매우 잘못되었습니다 (같은 이유로 말할 수 없음 B myBObj = new A()). 따라서 Java는이를 허용하지 않습니다. 오버로드 된 메서드에 의해 throw되는 확인되지 않은 예외는 동일하거나 하위 클래스이거나 존재하지 않아야합니다.


class Parent {void method () throws IndexOutOfBoundsException {System.out.println ( "Parent method"); }} class Child extends Parent {void method () throws RuntimeException {System.out.println ( "Child method"); } 부모 클래스가 런타임 예외의 자식을 throw하고 자식이 런타임 예외 자체를 throw하는 경우. 유효합니까?
abhiagNitk

1

이를 이해하기 위해 일부 파일을 읽고 작업을 수행하고 class의 인스턴스를 반환하는 메서드를 Mammal정의 하는 클래스가있는 예를 고려해 보겠습니다 .readAndGetMammal

class Mammal {
    public Mammal readAndGet() throws IOException {//read file and return Mammal`s object}
}

클래스는 클래스를 Human확장 Mammal하고 readAndGet메서드를 재정 의하여 의 인스턴스 Human대신의 인스턴스를 반환합니다 Mammal.

class Human extends Mammal {
    @Override
    public Human readAndGet() throws FileNotFoundException {//read file and return Human object}
}

호출하려면 확인 된 예외와 포유류 가이를 던지기 때문에 readAndGet처리해야 합니다.IOExceptionreadAndMethod

Mammal mammal = new Human();
try {
    Mammal obj = mammal.readAndGet();
} catch (IOException ex) {..}

그리고 우리는 컴파일러 mammal.readAndGet()가 클래스의 객체에서 호출되고 있음을 알고 Mammal있지만 런타임 JVM 은을 보유하고 있기 때문에 mammal.readAndGet()클래스에서 호출에 대한 메서드 호출을 해결 합니다.Humanmammalnew Human()

방법 readAndMethod에서 Mammal던지고 IOException하고 있기 때문에 체크 예외 컴파일러는 우리가 전화를 할 때마다 그것을 잡기 위해 우리를 강제 readAndGetmammal

이제 readAndGetin Human이 다른 체크 된 예외 (예 : Exception)를 던지고 있다고 가정 하고 왜냐하면 is holding readAndGet의 인스턴스에서 호출 될 것이라는 것을 알고 있습니다.Humanmammalnew Human()

컴파일러의 경우 메서드가에서 호출 Mammal되므로 컴파일러는 우리에게만 처리하도록 강제 IOException하지만 런타임에 메서드가 Exception처리되지 않는 예외를 throw하고 메서드가 예외를 throw하면 코드가 중단된다는 것을 알고 있습니다 .

그렇기 때문에 컴파일러 수준 자체에서 방지되고 마지막에 JVM에서 처리되지 않으므로 새롭거나 더 광범위한 검사 예외를 throw 할 수 없습니다.

메서드를 재정의하는 동안 따라야하는 다른 규칙도 있으며 , 이유를 알기 위해 메서드 재정의 규칙 을 따라야하는 이유 에 대해 자세히 읽을 수 있습니다 .


0

우리는 아래에 어떤 설명을 기인합니까?

class BaseClass {

    public  void print() {
        System.out.println("In Parent Class , Print Method");
    }

    public static void display() {
        System.out.println("In Parent Class, Display Method");
    }

}


class DerivedClass extends BaseClass {

    public  void print() throws Exception {
        System.out.println("In Derived Class, Print Method");
    }

    public static void display() {
        System.out.println("In Derived Class, Display Method");
    }
}

DerivedClass.java 클래스는 print 메서드가 Exception을 throw하고 baseclass의 print () 메서드가 예외를 throw하지 않을 때 컴파일 타임 예외를 throw합니다.

Exception이 RuntimeException보다 좁다는 사실에이를 기인 할 수 있으며, No Exception (Runtime error), RuntimeException 및 해당 자식 예외가 될 수 있습니다.


0

하위 클래스의 재정의 메서드는 슈퍼 클래스 메서드의 확인 된 예외의 하위 클래스 인 여러 확인 된 예외 만 throw 할 수 있지만 슈퍼 클래스의 메서드의 확인 된 예외와 관련이없는 여러 확인 된 예외는 throw 할 수 없습니다.


0

Java는 클라이언트가 catch되는 항목을 제한 한다고 가정하기 때문에 부모 클래스에서 예외를 제한 할 수있는 선택권제공합니다 . 이럴 당신은 기본적으로해야 결코 클라이언트가 길 아래에 유연성을 필요로 할 수 있기 때문에,이 "기능"을 사용하지 않습니다.

Java는 잘못 설계된 오래된 언어입니다. 현대 언어에는 그러한 제한이 없습니다. 이 결함을 피하는 가장 쉬운 방법은 throw Exception항상 기본 클래스를 만드는 것 입니다. 클라이언트는 더 구체적인 예외를 던질 수 있지만 기본 클래스를 정말 광범위하게 만듭니다.


0

재정의 된 메서드에 대한 검사 및 검사되지 않은 예외 처리 규칙

- 부모 클래스 메서드가 예외를 선언하지 않으면 자식 클래스 재정의 메서드는 ,

 1. No exception or
 2. Any number of unchecked exception
 3. but strictly no checked exception

-부모 클래스 메서드가 확인되지 않은 예외를 선언하면 자식 클래스 재정의 메서드가 ,

 1. No exception or
 2. Any number of unchecked exception 
 3. but strictly no checked exception

- 부모 클래스 메서드가 확인 된 예외를 선언하면 자식 클래스 재정의 메서드는 ,

 1. No exception or
 2. Same checked exception or
 3. Sub-type of checked exception or
 4. any number of unchecked exception

위의 모든 결론은 부모 클래스의 메서드에서 확인 된 예외와 확인되지 않은 예외의 조합이 선언 된 경우에도 참입니다.

Ref

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.