Обзор ограничений, накладываемых на исключения, на примере наследования и реализации интерфейса

в 12:01, , рубрики: java, исключения, Песочница, Программирование, метки: , ,

Приветствую всех изучающих Java!
Как известно исключения в Java, при наследовании подчиняются требованию сужения спецификации, а не ее расширения, в отличие от обычных классов.
В книге Bruce Eckel «Thinking in Java, 4 ed.» приведено описание ограничений, накладываемых при использовании исключений. Пример кода описывает наследование от базового класса и одновременную реализацию интерфейса, причем у обоих имеются методы с одинаковой сингатурой, но разной спецификацией исключений. Попытки реализации методов интерфейса пересекаются с попытками одновременного переопределения методов базового класса, в результате которых делается упор на недопущение расширения спецификации исключений. Упрощенный пример кода, доносящий идею:

class BaseBallException extends Exception{}
class Foul extends BaseBallException{}
class Strike extends BaseBallException{}

abstract class Inning {
	//-----Methods----
	public Inning() throws BaseBallException{}
	public void event() throws BaseBallException{}
		
	public abstract void atBat() throws Strike, Foul;
}

class StormException extends Exception{}
class RainedOut extends StormException{}
class PopFoul extends Foul{}

interface Storm {
	public void event() throws RainedOut;
}

class StormyInning extends Inning implements Storm {
	//--------Methods-----
	public StormyInning() throws RainedOut, BaseBallException{}
	public StormyInning(String str) throws Foul, BaseBallException{}

	/**ошибка компиляции - реализация метода Storm.event() throws RainedOut пытается изменить спецификацию 
	* исключений метода Inning.event() throws BaseBallException на новое исключение RainedOut*/
	//! public void event() throws RainedOut{}

	/**требуется переопределение метода event() с пустой спецификацией исключений - это безболезненно в 
	* силу сужения спецификации и необходимо для реализации интерфейса. 
	* В данном случае - единственный вариант*/
	public void event() {}
	
	public void atBat() throws PopFoul {}	
}

Пример можно дополнить другими вариантами спецификаций исключений, перебрав некоторые интересные сочетания:

class BaseBallException extends Exception{}
class Foul extends BaseBallException{}
class Strike extends BaseBallException{}
class LightStrike extends Strike{}
class TwistedLightStrike extends LightStrike{}

abstract class Inning {
	//-----Methods----
	public Inning() throws BaseBallException{}
	public void event() throws BaseBallException{}
	public void event1() throws BaseBallException{}
	public void event2() {}
	public void event3() throws RainedOut{}
	public void event4() throws Strike{}
	public void event5() throws LightStrike{}
}

class StormException extends Exception{}
class RainedOut extends StormException{}
class Drizzle extends RainedOut{}
class PopFoul extends Foul{}

interface Storm {
	public void event() throws RainedOut;
	public void event1();
	public void event2() throws RainedOut;
	public void event3() throws RainedOut;
	public void event4() throws LightStrike;
	public void event5() throws Strike;
}

class StormyInning extends Inning implements Storm {
	//--------Methods-----
	public StormyInning() throws RainedOut, BaseBallException{}
	public StormyInning(String str) throws Foul, BaseBallException{}

	/**ошибка компиляции - реализация метода Storm.event() throws RainedOut пытается изменить спецификацию 
	* исключений метода Inning.event() throws BaseBallException на новое исключение RainedOut*/
	//! public void event() throws RainedOut{}

	/**ошибка компиляции - реализация метода Storm.event() throws RainedOut c заменой исключния RainedOut на
	* BaseBallException является попыткой изменить спецификацию исключений этого метода*/
	//! public void event() throws BaseBallException{}
	
	/**ошибка компиляции - смешаный вариант не проходит по причине обоюдных попыток расширить 
	* спецификацию исключений*/
	//! public void event() throws RainedOut, BaseBallException{}	

	/**Единственный и необходимый вариант*/
	public void event() {}	
	
	/**ошибка компиляции - метод интерфейса Storm.event1() не допускает расширения спецификации*/
	//! public void event1() throws BaseBallException {}

	/**Единственный и необходимый вариант - интерфейс требует реализации метода Storm.event1() и 
	* только с чистой спецификацией*/
	public void event1(){}
	

	/**ошибка компиляции - метод абстрактного базового класса Inning.event() сопротивляется расширению
	* спецификации*/
	//! public void event2() throws RainedOut{}

	/**Единственный однако не необходимый вариант реализации интерфейсного метода. 
	* Реализация может отсутствовать вовсе т.к. абстрактный базовый класс реализует интерфейсный метод
	* Storm.event2() throws RainedOut с сужением спецификации исключений*/
	//public void event2(){}
	
	
	/**Реализация метода Storm.event3() throws RainedOut может отсутствовать вовсе - абстрактный базовый 
	* класс реализует интерфейсный метод Storm.event3() throws RainedOut, спецификации исключений 
	* совпадают. Но можно и реализовать метод с исходной спецификацией исключений*/ 
	//public void event3() throws RainedOut{}

	/**также можно в соответствии с иерархией исключений реализовывать методы с последовательным 
	* сужением спецификации  исключений вплоть до чистой*/
	//public void event3() throws Drizzle{}
	//public void event3() {}


	/**Теперь ловим плечо, на котором может быть реализован метод event4()*/

	/**Реализация метода event4() требуется т.к. абстрактный базовый класс расширяет, а не сужает 
	* спецификацию исключений*/

	/**ошибка компиляции - попытка расширить спецификацию исключений метода интерфейса Storm.event4()*/
	//! public void event4() throws Strike{}
	
	/**Работает - спецификация метода абстрактного класса сузилась, интерфейса - соответствует*/
	//public void event4() throws LightStrike{}

	/**Работает - здесь и далее обе спецификации исключений сузились*/
	//public void event4() throws TwistedLightStrike{}
	public void event4() {}

	/**Идея как и для event4(), но на этот раз реализации не требуется - абстрактный класс сужает спецификацию 
	* и реализует интерфейсный метод*/
	//! public void event5() throws Strike{}	
	//public void event5() throws LightStrike{}
	//public void event5() throws TwistedLightStrike{}	
	//public void event5()
}

Очевидно, приведенные выше ограничения и фичи будет не лишним учитывать при проектировании иерархии классов для очередного проекта.

Автор: getmanartem

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js