Análise e teste de código
Utilização correta de tipos de dados e conversões de tipo
Processamento de campos de caracteres
Uso de push-down de código no ABAP SQL
Melhorar o desempenho da tabela interna
Implementar verificações de autorização
Projetando código efetivo orientado a objetos
Definir e trabalhar com classes de exceção
Adicionar documentação à codificação ABAP

Trabalhar com classes de exceção

Objective

After completing this lesson, you will be able to trabalhar com classes de exceção.

Tratamento de exceções

No início deste curso, analisamos o tratamento de erros usando as instruções TRY, CATCH e ENDTRY. Após a instrução TRY vem a instrução que pode causar um erro. Se o erro ocorrer, o sistema salta para a instrução CATCH e processa o respectivo conteúdo. O que quer que aconteça, o método continua o processamento após a instrução ENDTRY.

Em um bloco TRY…ENDTRY, você pode incluir mais de uma instrução CATCH. Isso permite que você trate diferentes exceções de forma diferente.

Você pode listar várias exceções em uma única instrução CATCH. Neste exemplo, o mesmo bloco CATCH será processado se o sistema acionar cx_sy_arithmetic_error ou cx_sy_zerodivide.

Quando você lista uma classe em uma instrução CATCH, o sistema processará o bloco correspondente se uma exceção com essa classe ou uma de suas subclasses for acionada. Neste exemplo, cx_sy_arithmetic_error é uma classe superior de cx_sy_arithmetic_overflow e cx_sy_zerodivide. Em simultâneo, o bloco será processado se for criada uma exceção com o tipo de uma das subclasses.

Todas as classes de exceção fazem parte de uma hierarquia de herança cuja classe superior superior é a classe cx_root. Como já vimos, se você utilizar uma classe superior em uma instrução CATCH, o sistema irá para ela se uma exceção com o tipo de qualquer uma de suas subclasses for acionada. Uma vez que todas as classes de exceção são subclasses de cx_root, a instrução CATCH cx_root é suficiente para detectar qualquer exceção.

Sequência de instruções CATCH

Quando você escreve instruções CATCH, deve colocar primeiro a classe (ou classes) mais específicas e as mais genéricas por último. Se você colocar uma classe superior acima de uma de suas subclasses, causará um erro de sintaxe. Isso ocorre porque o sistema não conseguiu processar o bloco CATCH específico porque ele é ofuscado pelo mais genérico.

O suplemento INTO

Cada exceção é representada por um objeto. Você pode colocar esse objeto em uma variável de referência e trabalhar com ele - por exemplo, para recuperar o texto de erro da classe de exceção usando o método get_text( ), como mostrado no exemplo aqui.

Você pode utilizar uma declaração inline na instrução CATCH para garantir que a variável de referência seja declarada com o tipo correto.

A coisa mais comum a ver com o objeto de exceção é chamar seu método get_text( ). No entanto, os objetos de exceção podem ter outros atributos e métodos para ajudar você a tratar o erro.

As classes de exceção podem ter atributos que descrevem com mais precisão o que deu errado. Esses atributos são frequentemente utilizados para preencher os caracteres de preenchimento de mensagens. Você define um atributo na seção pública da classe de exceção com o respectivo nome e tipo adequados.

Para preencher um atributo quando a exceção é criada, você precisa de um parâmetro de importação correspondente do construtor da classe de exceção. Você pode gerar este parâmetro de importação utilizando uma correção rápida. Posicione o cursor no nome do método constructor e pressione Ctrl + 1. Escolha a correção rápida Regerar construtor.

Propagação de exceções

Um método pode propagar uma exceção ao longo da cadeia de chamadas. Em vez de usar a instrução CATCH para tratar a exceção, o método inclui a cláusula RAISING em sua definição. Isso indica aos chamadores do método que a exceção pode ser transferida para eles.

Se a exceção não for detectada em nenhum lugar ao longo da cadeia de chamadas, ocorrerá um erro em tempo de execução.

O atributo previous

Às vezes, dentro de uma cadeia de chamadas, um método interceptará uma exceção e, em resposta, ele levantará uma exceção diferente para seu próprio chamador pegar. Isso pode acontecer, por exemplo, se um framework criar uma exceção muito orientada tecnicamente para a aplicação processar. O método que aciona sua própria exceção pode transferir a primeira exceção. Isso pode ser efetuado utilizando o atributo previous da classe de exceção.

Quando você dispara uma exceção, pode transferir uma instância de uma classe de exceção para o parâmetro de importação previous. Isso anexa o objeto de exceção à nova exceção. Um método que captura a segunda exceção pode acessar o primeiro objeto de exceção usando o atributo público protegido contra gravação previous.

Como previous está implementado na classe superior cx_root, ele está disponível em todas as classes de exceção.

Testar: classes de exceção

  1. Crie uma nova classe que implemente a interface IF_OO_ADT_CLASSRUN.
  2. Copie o seguinte snippet de código para a parte de implementação do método if_oo_adt_classrun~main( ).
    Code Snippet
    12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
    DATA number1 TYPE i VALUE 2000000000. DATA number2 TYPE p LENGTH 2 DECIMALS 1 VALUE '0.5'. DATA result TYPE i. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow. out->write( 'Arithmetic Overflow' ). CATCH cx_sy_zerodivide. out->write( 'Division by zero' ). ENDTRY. number2 = 0. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow. out->write( 'Arithmetic Overflow' ). CATCH cx_sy_zerodivide. out->write( 'Division by zero' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_overflow cx_sy_zerodivide. out->write( 'Arithmetic overflow or division by zero' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_sy_arithmetic_error. out->write( 'Caught both exceptions using their common superclass' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_root. out->write( 'Caught any exception using CX_ROOT' ). ENDTRY. TRY. result = number1 / number2. CATCH cx_root into data(Exception). out->write( 'Used INTO to intercept the exception object' ). out->write( 'The get_Text( ) method returns the following error text: ' ). out->write( Exception->get_text( ) ). ENDTRY.
  3. Ative a classe com Ctrl + F3.
  4. Jogue com a codificação para se familiarizar com ela.

Trabalhar com objetos de exceção

Você melhora seu código acessando objetos de exceção, ou seja, instâncias de classes de exceção. Você utiliza parâmetros de construtor para definir valores de atributo de objetos de exceção.

Modelo:

  • /LRN/CL_S4D401_OOS_FACTORY (classe global)

Solução:

  • /LRN/CL_S4D401_EXS_HANDLING (classe global)

Tarefa 1: Copiar modelo (opcional)

Copie a classe modelo /LRN/CL_S4D401_OOS_FACTORY. Se você tiver concluído o exercício anterior, pode ignorar esta tarefa e continuar processando sua classe ZCL_##_SOLUTION.

Etapas

  1. Copie a classe /LRN/CL_S4D401_OOS_FACTORY para uma classe em seu próprio pacote (nome sugerido: ZCL_##_SOLUTION, onde ## representa seu número de grupo).

    1. No Explorador de projetos, clique com o botão direito na classe /LRN/CL_S4D401_OOS_FACTORY para abrir o menu de contexto.

    2. No menu de contexto, selecione Duplicar....

    3. Insira o nome do seu pacote no campo Pacote. No campo Nome, insira ZCL_##_SOLUTION, em que ## representa seu número de grupo.

    4. Selecione Avançar.

    5. Confirme a ordem de transporte e selecione Concluir.

  2. Ative a cópia.

    1. Pressione Ctrl + F3 para ativar a classe.

Tarefa 2: Acessar objetos de exceção

Na sua classe global, ajuste a implementação do método IF_OO_ADT_CLASSRUN~MAIN. Amplie o tratamento de exceções ao chamar o método factory GET_INSTANCE. Em ambos os blocos CATCH, receba uma referência ao objeto de exceção e extraia o texto de exceção. Substitua o texto literal para a saída do console pelos textos de exceção.

Etapas

  1. Navegue para a implementação do método IF_OO_ADT_CLASSRUN~MAIN da sua classe global.

    1. Abra a classe global e mude para a guia Classe global.

  2. Na primeira instrução CATCH, deixe um ponto de variável de referência no objeto de exceção, que é a instância da classe de exceção CX_ABAP_INVALID_VALUE (nome sugerido para a variável: exc_inv).

    Dica

    Você pode utilizar uma declaração inline para declarar a variável de referência com o tipo correto.
    1. Ajuste o código da seguinte forma:

      ABAP
      1
      CATCH cx_abap_invalid_value INTO DATA(exc_inv).
    2. Para exibir o tipo de variável exc_inv, posicione o cursor em exc_inv e pressione F2.

  3. No bloco CX_ABAP_INVALID_VALUE CATCH, comente a chamada do método out->write. Recupere o texto de exceção do objeto de exceção e utilize-o em uma nova chamada do método out->write.

    1. Ajuste o código da seguinte forma:

      ABAP
      123
      CATCH cx_abap_invalid_value INTO DATA(exc_inv). * out->write( |Carrier { c_carrier_id } does not exist| ). out->write( exc_inv->get_text( ) ).
  4. Faça o mesmo com a segunda instrução CATCH e o bloco CATCH (nome sugerido para a variável de referência: exc_auth).

    1. Ajuste o código da seguinte forma:

      ABAP
      123
      CATCH cx_abap_auth_check_exception INTO DATA(exc_auth). * out->write( |No authorization to display carrier { c_carrier_id }| ). out->write( exc_auth->get_text( ) ).
  5. Ative e teste seu código. Ajuste o valor da constante c_carrier_id para provocar as diferentes exceções.

    1. Pressione Ctrl + F3 para ativar o código.

    2. Pressione F9 para executar o código como aplicativo de console.

    3. Defina o valor da constante c_carrier_id como XX para provocar a exceção para o valor inválido.

    4. Defina o valor da constante c_carrier_id como UA para provocar a exceção para autorização em falta.

Tarefa 3: Tratar grupos de exceções

Na classe de teste local LTCL_FIND_FLIGHTS, ajuste a implementação do método CLASS_SETUP. Ao chamar o método factory GET_INSTANCE, processe todas as exceções possíveis em apenas um bloco CATCH. No caso de uma exceção, forneça o método de falha com o texto de exceção do objeto de exceção em vez da mensagem de erro genérica "Impossível instanciar lcl_carrier".

Etapas

  1. Navegue para a implementação do método CLASS_SETUP da classe de teste local LTCL_FIND_FLIGHTS.

    1. Mude para a ficha de registro Classes de teste.

    2. Role para a instrução METHOD class_setup.

  2. Após a chamada do método GET_INSTANCE, ajuste o tratamento de exceções. Comente o bloco de interceptação para a classe de exceção CX_ABAP_INVALID_VALUE e substitua a classe de exceção CX_ABAP_AUTH_CHECK_EXCEPTION por CX_ROOT.

    1. Ajuste o código da seguinte forma:

      ABAP
      1234567
      TRY. the_carrier = lcl_carrier=>get_instance( i_carrier_id = some_flight_data-carrier_id ). * CATCH cx_abap_invalid_value. * cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). CATCH cx_root. cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ). ENDTRY.
  3. Adicione uma cláusula INTO à instrução CATCH com uma variável de referência declarada inline (nome sugerido para a variável: exc_root).

    1. Ajuste o código da seguinte forma:

      ABAP
      1
      CATCH cx_root INTO DATA(exc_root).
    2. Para exibir o tipo de variável exc_root, posicione o cursor em exc_root e pressione F2.

  4. No bloco CX_ROOT CATCH, utilize o texto de exceção como entrada para o método CL_ABAP_UNIT_ASSERT=>falha.

    1. Ajuste o código da seguinte forma:

      ABAP
      12
      CATCH cx_root INTO DATA(exc_root). cl_abap_unit_assert=>fail( exc_root->get_text( ) ).
  5. Ative seu código.

    1. Pressione Ctrl + F3.

Tarefa 4: Definir atributos de objetos de exceção

Ajuste o acionamento das exceções no método factory GET_INSTANCE. Se você acionar a exceção CX_ABAP_INVALID_VALUE, utilize um parâmetro de construtor adequado para incluir o ID de agente de frete inválido no texto de exceção. Onde você acionar a exceção CX_ABAP_AUTH_CHECK_EXCEPTION, utilize um parâmetro de construtor apropriado para selecionar um texto de exceção mais apropriado.

Etapas

  1. Navegue para a implementação do método GET_INSTANCE da classe local LCL_CARRIER.

    1. Por exemplo, no método IF_OO_ADT_CLASSRUN~MAIN, posicione o cursor em get_instance e pressione F3.

  2. Analise a assinatura do método CONSTRUCTOR da classe de exceção CX_ABAP_INVALID_VALUE.

    1. Na instrução RAISE EXCEPTION, posicione o cursor em CX_ABAP_INVALID_VALUE e pressione F2 para exibir as informações do elemento ABAP para a classe de exceção.

    2. Na caixa de diálogo, selecione o construtor para exibir as informações do elemento para o método CONSTRUCTOR.

  3. Ajuste a instrução RAISE EXCEPTION para a classe de exceção CX_ABAP_INVALID_VALUE. Forneça o valor de parâmetro do construtor com o ID de transportadora inválido.

    Nota

    Você deve executar uma conversão de tipo porque o valor e i_carrier_id não têm um tipo idêntico.
    1. Ajuste o código da seguinte forma:

      ABAP
      123
      RAISE EXCEPTION TYPE cx_abap_invalid_value EXPORTING value = CONV #( i_carrier_id ).
  4. Ajuste a instrução RAISE EXCEPTION para a classe de exceção CX_ABAP_AUTH_CHECK_EXCEPTION. Forneça o parâmetro do construtor textid com a constante pública missing_Authorization dessa classe de exceção.

    1. Ajuste o código da seguinte forma:

      ABAP
      1234
      RAISE EXCEPTION TYPE cx_abap_auth_check_exception EXPORTING textid = cx_abap_auth_check_exception=>missing_authorization.
  5. Ative e teste seu código. Ajuste o valor da constante c_carrier_id para provocar as diferentes exceções.

    1. Pressione Ctrl + F3 para ativar o código.

    2. Pressione F9 para executar o código como aplicativo de console.

    3. Defina o valor da constante c_carrier_id como XX para provocar a exceção para o valor inválido.

    4. Defina o valor da constante c_carrier_id como UA para provocar a exceção para autorização em falta.