Java: Използване на Catch и хвърляне в един блок?

Какъв е смисълът от catching и след това също throwing и Exception като по-долу? Лоша практика ли е да се правят и двете?

  try{

    //something

    } catch (Exception e){

    throw new RuntimeException("reason for exception");
    }

person rni902    schedule 26.10.2015    source източник
comment
Повечето хора просто правят e.printStackTrace() в своите catch блокове. Откъде взехте това/защо хвърляте ново изключение? Смисълът на прихващането е да се предотврати срив, а не да се срине програмата с напълно ново изключение, което не съдържа нищо от оригиналната информация.   -  person Arc676    schedule 26.10.2015
comment
@Arc676 Повечето хора не правят e.printStackTrace(). Всъщност никога не съм писал това и никога не бих го препоръчал: трябва или да влезете правилно, или да хвърлите отново.   -  person Tunaki    schedule 26.10.2015
comment
Това обикновено се прави в случаите, когато методът не трябва да хвърля изключение (няма клауза за хвърляния), но все пак сте попаднали в невъзстановима ситуация, която трябва да прекратите, като хвърлите изключение ...   -  person Codebender    schedule 26.10.2015
comment
Взимам го обратно тогава.   -  person Arc676    schedule 26.10.2015


Отговори (3)


Обикновено такъв код се използва за повторно обвиване на изключенията, което означава трансформиране на типа на изключението. Обикновено правите това, когато сте ограничени в това какви изключения са разрешени извън вашия метод, но вътрешно могат да се случат други видове изключения. Например:

class MyServiceImplementaiton implements MyService {
    void myService() throws MyServiceException { // cannot change the throws clause here
        try {
            .... // Do something
        } catch(IOException e) {
            // re-wrap the received IOException as MyServiceException
            throw new MyServiceException(e); 
        }
    }
}

Този идиом позволява да продължите да разпространявате изключения към повикващия, като същевременно се съобразявате с клаузата за хвърляния в интерфейса и скривате подробностите за вътрешните елементи (фактът, че могат да се случат IOExceptions).

На практика това винаги е по-добре от просто извикване на e.printStackTrace(), което всъщност ще "погълне" условието за грешка и ще остави останалата част от програмата да работи, сякаш нищо не се е случило. В това отношение поведението на Eclipse е доста лошо, тъй като има тенденция да пише автоматично такива конструкции с лоша практика, ако разработчикът не е внимателен.

person secolive    schedule 26.10.2015

Това се нарича повторно хвърляне на изключение и е често срещан модел.

Позволява ви да промените класа на изключението (както в този случай) или да добавите повече информация (тук също така, стига този низ за грешка да има смисъл).

Често е добра идея да прикачите оригиналното изключение:

throw new RuntimeException("cause of the problem", e);

Повторното хвърляне като непроверено изключение (RuntimeException) понякога е необходимо, когато все още искате да хвърлите изключение, но API на вашия метод не позволява проверено изключение.

person Thilo    schedule 26.10.2015
comment
Мисля, че повторното хвърляне на изключение прави throw e;... OP просто създава ново изключение (от catch блок)... - person Codebender; 26.10.2015
comment
@Codebender: Бих включил и този модел. Хванете изключение, върнете обратно изключение. Вижте и stackoverflow.com/a/11972811/14955 - person Thilo; 26.10.2015
comment
Благодаря ви, какво се случва под капака, когато даден метод бъде хвърлен отново? Не разбирам концепцията? - person rni902; 26.10.2015
comment
Нищо особено наистина. Просто нормално улавяне и нормално хвърляне. Ефектът е, че все още разпространявате изключение, но можете да го оформите както сметнете за добре. - person Thilo; 27.10.2015

Във вашия пример Exception е уловен и RuntimeException е хвърлен, което ефективно замества (потенциално) проверено изключение с непроверено изключение, което не трябва да се обработва от повикващия, нито да се декларира от хвърлящия метод в клауза throws.

Няколко примера :

Този код преминава компилация:

public void SomeMethod ()
{
    try {

        //something

    } catch (Exception e){
        throw new RuntimeException("reason for exception");
    }
}

Този код не преминава компилация (ако приемем, че "нещо" може да хвърли проверено изключение):

public void SomeMethod ()
{
    //something
}

Алтернатива на улавянето на Exception и хвърлянето на непроверено изключение (т.е. RuntimeException) е да добавите throws клауза:

public void SomeMethod () throws Exception
{
    //something
}

Това е един случай на използване на улавяне на един тип изключение и хвърляне на друго. Друг случай на използване е да хванете един тип изключение и да хвърлите друг тип проверено изключение (което вашият метод декларира в своята клауза throws). Понякога се прави, за да се групират множество изключения, които могат да бъдат хвърлени вътре в метод, и да се хвърли само един тип изключение към извикващия метода (което ги улеснява да напишат кода за обработка на изключения и има смисъл, ако всички тези изключения трябва да се третират по същия начин).

person Eran    schedule 26.10.2015
comment
Благодаря за подробния отговор. Не разбирам защо това се предпочита пред просто хвърляне на оригиналното изключение? т.е. хвърляне на изключение, а не на RuntimeException? - person rni902; 26.10.2015
comment
Не можете винаги да хвърляте проверено изключение. Това трябва да стане част от вашия подпис на метода и това може да не е възможно (заради наследяване например). Винаги можете да хвърляте непроверени изключения (RuntimeException). - person Thilo; 27.10.2015