java违例的限制
包围一个要领时,只能发生已在要领的基本类版本中界说的违例。这是一个重要的限制,因为它意味着与基本类协同事情的代码也会自动应用于从基本类衍生的任何工具(虽然,这属于根基的OOP观念),个中包罗违例。
下面这个例子演示了强加在违例身上的限制范例(在编译期):
//: StormyInning.java // Overridden methods may throw only the // exceptions specified in their base-class // versions, or exceptions derived from the // base-class exceptions. class BaseballException extends Exception {} class Foul extends BaseballException {} class Strike extends BaseballException {} abstract class Inning { Inning() throws BaseballException {} void event () throws BaseballException { // Doesn't actually have to throw anything } abstract void atBat() throws Strike, Foul; void walk() {} // Throws nothing } class StormException extends Exception {} class RainedOut extends StormException {} class PopFoul extends Foul {} interface Storm { void event() throws RainedOut; void rainHard() throws RainedOut; } public class StormyInning extends Inning implements Storm { // OK to add new exceptions for constructors, // but you must deal with the base constructor // exceptions: StormyInning() throws RainedOut, BaseballException {} StormyInning(String s) throws Foul, BaseballException {} // Regular methods must conform to base class: //! void walk() throws PopFoul {} //Compile error // Interface CANNOT add exceptions to existing // methods from the base class: //! public void event() throws RainedOut {} // If the method doesn't already exist in the // base class, the exception is OK: public void rainHard() throws RainedOut {} // You can choose to not throw any exceptions, // even if base version does: public void event() {} // Overridden methods can throw // inherited exceptions: void atBat() throws PopFoul {} public static void main(String[] args) { try { StormyInning si = new StormyInning(); si.atBat(); } catch(PopFoul e) { } catch(RainedOut e) { } catch(BaseballException e) {} // Strike not thrown in derived version. try { // What happens if you upcast? Inning i = new StormyInning(); i.atBat(); // You must catch the exceptions from the // base-class version of the method: } catch(Strike e) { } catch(Foul e) { } catch(RainedOut e) { } catch(BaseballException e) {} } } ///:~
在Inning中,可以看到无论构建器照旧event()要领都指出本身会“掷”出一个违例,但它们实际上没有那样做。这是正当的,因为它答允我们强迫用户捕捉大概在包围过的event()版本里添加的任何违例。同样的原理也合用于abstract要领,就象在atBat()里展示的那样。
“interface Storm”很是有趣,因为它包括了在Incoming中界说的一个要领——event(),以及不是在个中界说的一个要领。这两个要领城市“掷”出一个新的违例范例:RainedOut。当执行到“StormyInning extends”和“implements Storm”的时候,可以看到Storm中的event()要领不能改变Inning中的event()的违例接口。同样地,这种设计是十分公道的;不然的话,当我们操纵基本类时,便基础无法知道本身捕捉的是否正确的对象。虽然,如果interface中界说的一个要领不在基本类里,好比rainHard(),它发生违例时就没什么问题。
对违例的限制并不合用于构建器。在StormyInning中,我们可看到一个构建器可以或许“掷”出它但愿的任何对象,无论基本类构建器“掷”出什么。然而,由于必需僵持按某种方法挪用基本类构建器(在这里,会自动挪用默认构建器),所以衍生类构建器必需在本身的违例类型中声明所有基本类构建器违例。
StormyInning.walk()不会编译的原因是它“掷”出了一个违例,而Inning.walk()却不会“掷”出。若答允这种环境产生,就可让本身的代码挪用Inning.walk(),并且它不必节制任何违例。但在今后替换从Inning衍生的一个类的工具时,违例就会“掷”出,造成代码执行的间断。通过强迫衍生类要领遵守基本类要领的违例类型,工具的替换可保持连贯性。
包围过的event()要领向我们显示出一个要领的衍生类版本可以不发生任何违例——即便基本类版本要发生违例。同样地,这样做是须要的,因为它不会间断那些已假定基本类版本会发生违例的代码。差不多的原理亦合用于atBat(),它会“掷”出PopFoul——从Foul衍生出来的一个违例,而Foul违例是由atBat()的基本类版本发生的。这样一来,如果有人在本身的代码里操纵Inning,同时挪用了atBat(),就必需捕捉Foul违例。由于PopFoul是从Foul衍生的,所以违例节制器(模块)也会捕捉PopFoul。
最后一个有趣的处地址main()内部。在这个处所,如果我们明晰操纵一个StormyInning工具,编译器就会强迫我们只捕捉特定于谁人类的违例。但如果我们上溯造型到基本范例,编译器就会强迫我们捕捉针对基本类的违例。通过所有这些限制,违例节制代码的“结实”水平得到了大幅度改进(注释③)。
③:ANSI/ISO C++施加了雷同的限制,要求衍生要领违例与基本类要领掷出的违例沟通,可能从后者衍生。在这种环境下,C++实际上可以或许在编译期间查抄违例类型。
我们必需认识到这一点:尽量违例类型是由编译器在担任期间强行遵守的,但违例类型并不属于要领范例的一部门,后者仅包罗了要领名以及自变量范例。因此,我们不行在违例类型的基本上包围要领。除此以外,尽量违例类型存在于一个要领的基本类版本中,但并不暗示它必需在要领的衍生类版本中存在。这与要领的“担任”颇有差异(举办担任时,基本类中的要领也必需在衍生类中存在)。换言之,用于一个特定要领的“违例类型接口”大概在担任和包围时变得更“窄”,但它不会变得更“宽”——这与担任时的类接口法则是正好相反的。