Warum können überschreibende Methoden keine Ausnahmen auslösen, die breiter sind als die überschriebene Methode?

104

Ich habe das SCJP 6-Buch von Kathe sierra durchgesehen und bin auf diese Erklärungen gestoßen, Ausnahmen in überschriebener Methode zu werfen. Ich habe es nicht verstanden. Kann mir jemand das erklären?

Die überschreibende Methode darf KEINE geprüften Ausnahmen auslösen, die neu oder umfassender sind als die von der überschriebenen Methode deklarierten. Beispielsweise kann eine Methode, die eine FileNotFoundException deklariert, nicht von einer Methode überschrieben werden, die eine SQLException, Exception oder eine andere Nicht-Laufzeit-Ausnahme deklariert, es sei denn, es handelt sich um eine Unterklasse von FileNotFoundException.

Arpanoid
quelle
1
Hier ist eine Seite, die Sie vielleicht hilfreich finden: javapractices.com/topic/TopicAction.do?Id=129
Tim Bish

Antworten:

155

Wenn eine Methode deklariert, eine bestimmte Ausnahme auszulösen, kann die überschreibende Methode in einer Unterklasse nur deklarieren, dass diese Ausnahme oder ihre Unterklasse ausgelöst wird. Beispielsweise:

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, aber SQLExceptionnicht.

Dies liegt am Polymorphismus:

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

Wenn Sie Bsich für einen Wurf entschieden hätten, SQLExceptionkönnte der Compiler Sie nicht zwingen, ihn abzufangen, da Sie sich Bdurch seine Oberklasse auf die Instanz von beziehen - A. Auf der anderen Seite wird jede Unterklasse von IOExceptiondurch Klauseln (catch oder throw) behandelt, die diese behandelnIOException

Die Regel, die Sie benötigen, um Objekte nach ihrer Oberklasse referenzieren zu können, ist das Liskov-Substitutionsprinzip.

Da ungeprüfte Ausnahmen überall ausgelöst werden können, unterliegen sie nicht dieser Regel. Sie können der Throws-Klausel eine ungeprüfte Ausnahme als Dokumentationsform hinzufügen, wenn Sie möchten, aber der Compiler erzwingt nichts dagegen.

Bozho
quelle
Dies gilt auch bei der Implementierung von Schnittstellen? Ich bin mir nicht sicher, ob die Implementierung einer Schnittstelle immer noch als "Überschreiben" bezeichnet wird.
Muhammad Gelbana
Wie wäre es mit @Override public void foo () {..} Ich weiß, dass es erlaubt ist, aber die Erklärung für diesen Fall ist nicht klar.
Nascar
4
@danip Die überschreibende Methode kann eine beliebige Teilmenge von Ausnahmen auslösen, die von der überschriebenen Methode ausgelöst werden. Die leere Menge ist ebenfalls eine Teilmenge. Deshalb @Override public void foo() {...}ist legal.
Entwickler Marius Žilėnas
@Bozho sollte es nicht sein, wenn eine Methode deklariert, eine bestimmte Ausnahme
auszulösen
Wie ist das Überwunden in der realen Welt? Ich muss eine Methode aus einer implementierten Schnittstelle überschreiben, aber meine Implementierung enthält eine Auslöseverzögerung, die der Schnittstelle jedoch nicht. Was ist hier das Standardverfahren?
ap
22

Die überschreibende Methode KANN jede ungeprüfte (Laufzeit-) Ausnahme auslösen, unabhängig davon, ob die überschriebene Methode die Ausnahme deklariert

Beispiel:

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()");
    }
}
syrus.phoenix
quelle
Wie erzwingen Sie einen Fehler oder eine Warnung, wenn Ihre Schnittstelle keine Laufzeitausnahme deklariert, die die Unterklasse ausführt? Ich versuche, die Konsistenz zu Dokumentationszwecken zu erzwingen. Es ist einfacher, den Schnittstellentyp auf alle Ausnahmen zu überprüfen, die aktiviert und deaktiviert sind, als den Schnittstellentyp zu finden und dann in die Implementierung einzutauchen, um festzustellen, ob IOException oder IllegalArgumentException ausgelöst wird.
anon58192932
14

Meiner Meinung nach ist es ein Fehler im Java-Syntaxdesign. Polymorphismus sollte die Verwendung der Ausnahmebehandlung nicht einschränken. Tatsächlich tun es andere Computersprachen nicht (C #).

Darüber hinaus wird eine Methode in einer spezielleren Unterklasse überschrieben, so dass sie komplexer ist und aus diesem Grund wahrscheinlicher neue Ausnahmen auslöst.

Caligari
quelle
8

Ich gebe diese Antwort hier auf die alte Frage, da keine Antworten darauf hinweisen, dass die überschreibende Methode hier nichts mehr werfen kann, was die überschreibende Methode werfen kann:

1) werfen Sie die gleiche Ausnahme

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) Wirf eine Unterklasse der ausgelösten Ausnahme der Overriden-Methode

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) nichts werfen.

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) RuntimeExceptions in Würfen sind nicht erforderlich.

Es kann RuntimeExceptions in Würfen geben oder nicht, der Compiler wird sich nicht darüber beschweren. RuntimeExceptions sind keine geprüften Ausnahmen. Es sind nur aktivierte Ausnahmen erforderlich, um in Würfen zu erscheinen, wenn sie nicht gefangen werden.

Entwickler Marius Žilėnas
quelle
6

Um dies zu veranschaulichen, betrachten Sie:

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);
  }
}

Angenommen, Sie schreiben dann:

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

Dies führt zu einem Kompilierungsfehler, da r.close () eine IOException auslöst, die breiter als FileNotFoundException ist.

Um dies zu beheben, wenn Sie schreiben:

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

Sie erhalten einen anderen Kompilierungsfehler, weil Sie die Operation perform (...) implementieren, aber eine Ausnahme auslösen, die nicht in der Definition der Methode durch die Schnittstelle enthalten ist.

Warum ist das wichtig? Nun, ein Verbraucher der Schnittstelle kann haben:

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

Wenn die IOException ausgelöst werden durfte, ist der Code des Clients nicht mehr korrekt.

Beachten Sie, dass Sie diese Art von Problem vermeiden können, wenn Sie nicht aktivierte Ausnahmen verwenden. (Ich schlage nicht vor, dass Sie es tun oder nicht, das ist eine philosophische Frage)

Dilum Ranatunga
quelle
3

Lassen Sie uns ein Interview Frage nehmen. Es gibt eine Methode, die NullPointerException in der Oberklasse auslöst. Können wir es mit einer Methode überschreiben, die RuntimeException auslöst?

Um diese Frage zu beantworten, teilen Sie uns mit, was eine nicht aktivierte und aktivierte Ausnahme ist.

  1. Überprüfte Ausnahmen müssen explizit abgefangen oder weitergegeben werden, wie unter Grundlegende Behandlung von try-catch-finally-Ausnahmen beschrieben. Nicht aktivierte Ausnahmen haben diese Anforderung nicht. Sie müssen nicht gefangen oder für geworfen erklärt werden.

  2. Überprüfte Ausnahmen in Java erweitern die Klasse java.lang.Exception. Nicht aktivierte Ausnahmen erweitern die java.lang.RuntimeException.

Die öffentliche Klasse NullPointerException erweitert die RuntimeException

Nicht aktivierte Ausnahmen erweitern die java.lang.RuntimeException. Deshalb ist NullPointerException eine Uncheked-Ausnahme.

Nehmen wir ein Beispiel: Beispiel 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
    }
}

Das Programm wird erfolgreich kompiliert. Beispiel 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
    }
}

Das Programm wird auch erfolgreich kompiliert. Daher ist es offensichtlich, dass bei ungeprüften Ausnahmen nichts passiert. Schauen wir uns nun an, was bei geprüften Ausnahmen passiert. Beispiel 3: Wenn sowohl die Basisklasse als auch die untergeordnete Klasse eine aktivierte Ausnahme auslösen

    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);
        }

    }
}

Das Programm wird erfolgreich kompiliert. Beispiel 4: Wenn die untergeordnete Klassenmethode eine randgeprüfte Ausnahme auslöst, verglichen mit derselben Methode der Basisklasse.

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);
        }

    }
}

Das Programm kann nicht kompiliert werden. Wir müssen also vorsichtig sein, wenn wir geprüfte Ausnahmen verwenden.

Soudipta Dutta
quelle
2

Angenommen, Sie haben eine Superklasse A mit der Methode M1, die E1 wirft, und die Klasse B, die von A abgeleitet ist, wobei die Methode M2 ​​M1 überschreibt. M2 kann nichts UNTERSCHIEDLICHES oder WENIGER SPEZIALISIERTES als E1 werfen.

Aufgrund des Polymorphismus sollte der Klient, der Klasse A verwendet, in der Lage sein, B so zu behandeln, als wäre es A. Inharitance ===> Is-a (B ist-a A). Was ist, wenn dieser Code, der sich mit Klasse A befasst, die Ausnahme E1 behandelt, da M1 deklariert, dass er diese überprüfte Ausnahme auslöst, dann aber ein anderer Ausnahmetyp ausgelöst wurde? Wenn M1 eine IOException auslösen würde, könnte M2 eine FileNotFoundException auslösen, da es sich um eine IOException handelt. Kunden von A konnten dies problemlos handhaben. Wenn die ausgelöste Ausnahme weiter gefasst wäre, hätten Kunden von A keine Chance, davon zu erfahren, und hätten daher keine Chance, sie zu fangen.

Peter Perháč
quelle
Perhac :: gilt dies sowohl für aktivierte als auch für deaktivierte Ausnahmen? oder variiert es?
Ylnsagar
@ylnsagar Dies ist nur für geprüfte Ausnahmen. Nicht aktivierte Ausnahmen (Untertypen von RuntimeException) können auch als "Programmiererfehler" bezeichnet werden und sollten in der Regel nicht abgefangen werden. Sie müssen daher nicht in der throw-Klausel deklariert werden. Eine ungeprüfte Ausnahme kann jederzeit von jedem Code aus auftreten. Die obige Diskussion
bezieht sich
@Perhac :: ja du hast recht, aber nach meinem Verständnis durch das Lesen von Artikeln. Dies gilt auch für ungeprüfte Ausnahmen. Zum Beispiel, wenn eine Superklassenmethode eine Nullzeigerausnahme auslöst und wenn die die Methode überschreibende Unterklasse eine Ausnahme auslöst. Hier ist Exception eine Superklasse von Null Pointer Exception. Dann würde der Compiler dies nicht zulassen.
Ylnsagar
1

Nun, java.lang.Exception erweitert java.lang.Throwable. java.io.FileNotFoundException erweitert java.lang.Exception. Wenn also eine Methode java.io.FileNotFoundException auslöst, können Sie in der Override-Methode nichts höher in der Hierarchie als FileNotFoundException auslösen, z. B. können Sie java.lang.Exception nicht auslösen. Sie können jedoch eine Unterklasse von FileNotFoundException auslösen. Sie wären jedoch gezwungen, die FileNotFoundException in der Überschreibungsmethode zu behandeln. Knock up ein Code und probieren Sie es aus!

Die Regeln sind vorhanden, damit Sie die ursprüngliche Wurfdeklaration nicht verlieren, indem Sie die Spezifität erweitern, da der Polymorphismus bedeutet, dass Sie die Überschreibungsmethode für die Oberklasse aufrufen können.

planetjones
quelle
1

Die überschreibende Methode darf KEINE geprüften Ausnahmen auslösen, die neu oder umfassender sind als die von der überschriebenen Methode deklarierten.

Beispiel:

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
    }
}
syrus.phoenix
quelle
1

Die überschreibende Methode darf KEINE geprüften Ausnahmen auslösen, die neu oder umfassender sind als die von der überschriebenen Methode deklarierten.

Dies bedeutet einfach, dass beim Überschreiben einer vorhandenen Methode die Ausnahme, die diese überladene Methode auslöst, entweder dieselbe Ausnahme sein sollte, die die ursprüngliche Methode auslöst, oder eine ihrer Unterklassen .

Beachten Sie, dass die Überprüfung, ob alle überprüften Ausnahmen behandelt werden, zur Kompilierungszeit und nicht zur Laufzeit erfolgt. Zur Kompilierungszeit selbst überprüft der Java-Compiler den Ausnahmetyp, den die überschriebene Methode auslöst. Da nur zur Laufzeit entschieden werden kann, welche überschriebene Methode ausgeführt wird, können wir nicht wissen, welche Art von Ausnahme wir abfangen müssen.


Beispiel

Nehmen wir an, wir haben eine Klasse Aund ihre Unterklasse B. Ahat Methode m1und Klasse Bhat diese Methode überschrieben (nennen wir sie m2, um Verwirrung zu vermeiden ..). Sagen wir jetzt m1Würfe E1und m2Würfe E2, das ist E1die Oberklasse. Jetzt schreiben wir den folgenden Code:

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

Beachten Sie, dass dies m1nichts anderes als ein Aufruf von ist m2(auch hier sind Methodensignaturen bei überladenen Methoden gleich, also verwechseln Sie sich nicht mit m1und m2... sie dienen nur zur Unterscheidung in diesem Beispiel ... beide haben dieselbe Signatur). Zum Zeitpunkt der Kompilierung geht der Java-Compiler jedoch nur zum Referenztyp ( Ain diesem Fall Klasse ). Er überprüft die Methode, ob sie vorhanden ist, und erwartet, dass der Programmierer sie verarbeitet. Sie werden also offensichtlich werfen oder fangen E1. Nun, zur Laufzeit, wenn die überladene Methode löst E2, was E1die Oberklasse ist, dann ... nun, es ist sehr falsch (aus dem gleichen Grund, den wir nicht sagen können B myBObj = new A()). Daher erlaubt Java dies nicht. Nicht aktivierte Ausnahmen, die von der überladenen Methode ausgelöst werden, müssen identisch, unterklassig oder nicht vorhanden sein.

Aniket Thakur
quelle
Klasse Parent {void method () löst IndexOutOfBoundsException aus {System.out.println ("Parent method"); }} Klasse Child erweitert Parent {void method () löst RuntimeException aus {System.out.println ("Child method"); } Wenn die übergeordnete Klasse ein untergeordnetes Element der Laufzeitausnahme auslöst und das untergeordnete Element die Laufzeitausnahme selbst auslöst. Ist es gültig?
abhiagNitk
1

Um dies zu verstehen, betrachten wir ein Beispiel, in dem wir eine Klasse haben, die eine Methode Mammaldefiniert readAndGet, die eine Datei liest, eine Operation daran ausführt und eine Instanz der Klasse zurückgibt Mammal.

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

Class Humanerweitert class Mammalund überschreibt die readAndGetMethode, um die Instanz von Humananstelle der Instanz von zurückzugeben Mammal.

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

Um anzurufen, müssen readAndGetwir behandeln, IOExceptionweil es eine überprüfte Ausnahme ist und Säugetiere readAndMethodes werfen.

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

Und wir wissen, dass der Compiler mammal.readAndGet()vom Objekt der Klasse aufgerufen wird, Mammalaber zur Laufzeit löst JVM den mammal.readAndGet()Methodenaufruf in einen Aufruf der Klasse auf, Humanweil er mammalhält new Human().

Verfahren readAndMethodvon Mammalwirft , IOExceptionund weil es eine geprüfte Ausnahme Compiler ist uns , es zu fangen zwingen , wenn wir rufen readAndGetaufmammal

Angenommen, readAndGetin Humanlöst eine andere aktivierte Ausnahme aus, z. B. Exception, und wir wissen readAndGet, dass sie von der Instanz aufgerufen wird, Humanweil weil mammalhält new Human().

Da für den Compiler die Methode aufgerufen Mammalwird, zwingt uns der Compiler, nur zu behandeln, IOExceptionaber zur Laufzeit wissen wir, dass die Methode eine ExceptionAusnahme auslöst, die nicht behandelt wird, und unser Code wird unterbrochen, wenn die Methode die Ausnahme auslöst.

Aus diesem Grund wird dies auf Compilerebene selbst verhindert, und wir dürfen keine neuen oder umfassenderen geprüften Ausnahmen auslösen, da dies am Ende nicht von JVM behandelt wird.

Es gibt auch andere Regeln, die wir beim Überschreiben der Methoden befolgen müssen, und Sie können mehr darüber lesen, warum wir Regeln zum Überschreiben von Methoden befolgen sollten , um die Gründe zu kennen.

Naresh Joshi
quelle
0

Welche Erklärung schreiben wir dem Folgenden zu?

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");
    }
}

Die Klasse DerivedClass.java löst eine Ausnahme zur Kompilierungszeit aus, wenn die Druckmethode eine Ausnahme auslöst. Die print () -Methode der Basisklasse löst keine Ausnahme aus

Ich kann dies auf die Tatsache zurückführen, dass Exception schmaler als RuntimeException ist. Es kann sich entweder um No Exception (Runtime-Fehler), RuntimeException und deren untergeordnete Ausnahmen handeln

abhi
quelle
0

Die überschreibende Methode der Unterklasse kann nur mehrere geprüfte Ausnahmen auslösen, die Unterklassen der geprüften Ausnahme der Oberklassenmethode sind, kann jedoch nicht mehrere geprüfte Ausnahmen auslösen, die nicht mit der geprüften Ausnahme der Oberklassenmethode zusammenhängen

Fego
quelle
0

Java gibt Ihnen die Möglichkeit , Ausnahmen in der übergeordneten Klasse einzuschränken, da davon ausgegangen wird, dass der Client das Abfangen einschränkt . Meiner Meinung nach sollten Sie diese "Funktion" im Wesentlichen niemals verwenden, da Ihre Kunden möglicherweise später Flexibilität benötigen.

Java ist eine alte Sprache, die schlecht gestaltet ist. Moderne Sprachen haben keine solchen Einschränkungen. Der einfachste Weg, um diesen Fehler zu umgehen, besteht darin, Ihre Basisklasse throw Exceptionimmer zu erstellen . Clients können spezifischere Ausnahmen auslösen, aber Ihre Basisklassen sind wirklich breit.

Jonathan
quelle
0

Regel für die Behandlung von Überprüfungen und nicht aktivierten Ausnahmen für überschriebene Methoden

- Wenn die übergeordnete Klassenmethode keine Ausnahme deklariert , kann die übergeordnete Methode der untergeordneten Klasse deklarieren :

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

-Wenn die übergeordnete Klassenmethode eine ungeprüfte Ausnahme deklariert , kann die überschreibende Methode der untergeordneten Klasse deklarieren :

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

- Wenn die Methode der übergeordneten Klasse die aktivierte Ausnahme deklariert , kann die Methode zum Überschreiben der untergeordneten Klasse deklarieren :

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

Alle obigen Schlussfolgerungen gelten auch dann, wenn die Kombination aus aktivierter und nicht aktivierter Ausnahme in der Methode der übergeordneten Klasse deklariert ist

Ref

Ramesh Papaganti
quelle