原文連結:Defining and Calling Methods
今天緊接著要來在calss內宣告方法。方法有分靜態方法(CLASS-METHODS)跟動態方法(METHODS)。下面為一個方法的宣告結構,在方法名稱後包含了要傳遞的參數以及例外宣告,每個參數都包含了名稱與型態,可以分成以下四種:
CLASS <class_name> DEFINITION.
<any>SECTION. "這裡是任意SECTION的宣告
METHODS <method_name>
"導入參數"
IMPORTING <input_1> TYPE<type>
<input_2> TYPE<type> DEFAULT <val>
...
"導出參數"
EXPORTING <output_1> TYPE<type>
...
"變更參數"
CHANGING <inout_1> TYPE <type> OPTIONAL
...
"返回參數"
RETURNING VALUE(result) TYPE<type>
RAISING <excepyion1>
<excepyion2>
...
ENDCLASS.
導入參數 Importing Parameters
從呼叫端可以取得的值,數量不限。
預設上,填入導入參數值是必要的,但是有兩個方式會讓導入參數值變成可選項:
1.OPTIONAL
註記,此時初始參數值將自動設為適合該參數的預設值
2.DEFAULT <val>
註記,將根據的型別賦予初始值
如果變更了導入參數的位置,可能導致語法錯誤。
導出參數Exporting Parameters
為方法的回傳結果,一樣數量不限。所有導出參數的值都是可選的。
變更參數 Changing Parameters
同樣是用來取得呼叫端提供的值,但與導入參數不同的是,函式可以對變更參數進行變更。數項不限且強制填值,但一樣可以參考導入參數,將填入值改成可選項。
返回參數 Returning Parameters
返回參數是函式運算的結果,但可以直接在表達式中使用。一個函式中僅能有一個返回參數,且必須使用pass-by-value,這種形式的參數傳遞被包圍在不含空格的括號中,並前綴VALUE
。
例外狀況RAISING
則用於列出函式運行時可能出現的例外狀況跟錯誤。
CLASS lcl_connection DEFINITION.
PUBLIC SECTION.
...
* 方法宣告(導入參數)
METHODS set_attributes
IMPORTING
i_carrier_id TYPE /dmo/carrier_id OPTIONAL
i_connection_id TYPE /dmo/connection_id.
* 方法宣告(導出參數)
METHODS get_attributes
EXPORTING
e_carrier_id TYPE /dmo/carrier_id
e_Connection_id TYPE /dmo/connection_id.
* PROTECTED SECTION.
* PRIVATE SECTION.
ENDCLASS.
如範例所示,在class lcl_connection中,分別用兩個不同的方法來導入與導出參數。
只要在 class 的 define 部分有宣告的函式,都必須在下一段的 implementation 部分裡建立對應的實體,否則會出現語法錯誤喔!
CLASS lcl_connection IMPLEMENTATION.
* 建立實體,輸入參數
METHOD set_attributes.
carrier_id = i_carrier_id.
connection_id = i_connection_id.
ENDMETHOD.
* 建立實體,輸出參數
METHOD get_attributes.
e_carrier_id = carrier_id.
e_connection_id = connection_id.
ENDCLASS.
上例由前面定義的方法來建立實體,在Implementation
區塊,使用METHOD <method_name>
跟ENDMETHOD.
將方法建立成建立函式實體。
和前一章不同,由於這裡的函式賦值時已在class內,故不需要屬性選擇器來指定class。
只要先前有定義過的方法都需要建立實體,並且可以存取先前所宣告的變數。但靜態方法僅能存取靜態參數。並且可以不使用參照變數及->
,=>
等識別符就訪問class裡的屬性。
如同其他程式語言的this
用於自我呼叫,ABAP 提供作用於方法實例裡的 built-in 變數 ME
。ME
是一種參照變數,型別參照當前的class,且其位址與當前實體的位置一致,建議上會盡量避免使用,除非有物件的屬性會名稱產生衝突。
*假設class內有變數與方法內變數撞名
DATA carrier_id TYPE /dmo/carrier_id.
DATA connection_id TYPE /dmo/connection_id.
...
METHODS set_attributes
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
i_connection_id TYPE /dmo/connection_id.
* 在函式實體中使用ME來避免混淆
METHOD set_attributes.
me->carrier_id = carrier_id.
me->connection_id = connection_id.
ENDMETHOD.
在範例中,set_attributes( )
的導入參數剛好和上面建立的物件屬性名稱相同,這時就可使用ME
來指定原先建立的內建參數避免混淆,並以->
連接變數名稱。
* 定義例外
RAISE EXCEPTION TYPE <exception_class>.
* 實例化例外
METHOD set_attributes.
IF carrier_id IS INITIAL OR connection_id IS INITIAL.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
me->carrier_id = carrier_id.
me->connection_id = connection_id.
ENDMETHOD.
當方法內的例外事件發生,通常會用if
指定觸發條件,再透過RAISE EXCEPTION TYPE
呼叫例外狀況。技術上而言,例外的名稱會是ABAP中特殊例外class的名稱,並透過RAISE EXCEPTION TYPE
建立該例外class的實體。
一旦例外事件發生,整個程式就會停止執行。只有當三個先決條件都被滿足時,控制權才會返回給呼叫的程式,否則會直接進runtime error。先決條件包含:
具體 TRY CATCH 寫法會在後續的實作階段提及。
* 動態函式
<reference_variable>-><method_name>(
EXPORTING <input_1> = <expression>
<input_2> = <expression>
...
IMPORTING <output_1> = <variable>
...
CHANGING <inout_1> = <variable>
...
).
* 靜態函式
<class_name>=><static_methood>( ... ).
如上例所示,根據是否為靜態方法分別用->
與=>
(無空格)對函式進行呼叫,並且參數包含在括號中,若無,則括號內至少要有一個空格。
注意,在呼叫方法時,EXPORTING
與IMPORTING
關鍵字底下的參數類型(導入/導出)與關鍵字的動作是相反的。原因是我們要「傳出(EXPORTING)」「導入參數(input parameter)」供其他方法使用,並且「接收(IMPORTING)」其他程式的「導出參數(output)」,我自己是記"導入(呼叫端的)參數"以及"(從呼叫端)導出(的)參數"。寫法如下:
* 呼叫函式
* 於`IMPORTING`字段,將參數從呼叫端導出,並"接收"回傳的值至函式的導出參數中
connection->get_attributes(
IMPORTING
e_carrier_id = carrier_id
e_connection_id = connection_id ).
* 呼叫函式
* 於`EXPORTING`字段,將函式的導入參數值"輸出",導入至呼叫端中
connection->set_attributes(
EXPORTING
i_carrier_id = 'LH'
i_connection_id = '0400').
另外,由於EXPORTING區塊的導入參數會直接被呼叫端導入,對導入參數賦值時可以使用表達式語法,只要資料型別與原始參數相符即可;但傳入的導出參數及變更參數必須使用陳述式,一樣要注意型別。
前面說過,方法中關鍵字IMPORTING是強制要有的,然而接收導入參數的EXPORTING則是可選項。在ABAP中,有幾種省略的寫法:
connection->set_attributes(
EXPORTING
carrier_id = 'LH'
connection_id = '0400').
connection->set_attributes(
carrier_id = 'LH'
connection_id = '0400').
connection->set_attributes('0400').
在稍早我們知道了如何在定義時用if … RAISE EXCEPTION TYPE
定義例外,而在呼叫時,使用TRY … ENDTRY
及CATCH
來定義例外,在CATCH
後要接想觸發的例外名稱(名稱宣告在class定義時的RAISING
區段中)。
* 呼叫函式(主程式)
TRY.
connection->set_attributes(
EXPORTING
i_carrier_id = c_carrier_id
i_connection_id = c_connection_id
).
APPEND connection TO connections.
out->write( `Method call successful` ).
CATCH cx_abap_invalid_value.
out->write( `Method call failed` ).
ENDTRY.
上述範例為主程式,如果 i_carrier_id
或i_connection_id
這兩個參數在賦值時出現型別錯誤,就會觸發例外警示。
* 定義方法(位於LocalType)
CLASS lcl_connection DEFINITION.
PUBLIC SECTION.
* 屬性宣告
DATA carrier_id TYPE /dmo/carrier_id.
DATA connection_id TYPE /dmo/connection_id.
* 方法宣告
METHODS set_attributes
IMPORTING
i_carrier_id TYPE /dmo/carrier_id DEFAULT 'LH'
i_Connection_id TYPE /dmo/connection_id
RAISING
cx_abap_invalid_value.
ENDCLASS.
* 實例化函式
CLASS lcl_connection IMPLEMENTATION.
METHOD set_attributes.
"設置觸發條件"
IF i_carrier_id IS INITIAL OR i_connection_id IS INITIAL.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
carrier_id = i_carrier_id.
connection_id = i_connection_id.
ENDMETHOD.
ENDCLASS.
第二段是LocalType裡的自訂義Class connection
,也就是宣告該方法的地方。這個範例中的參數、方法及例外都是宣告在connection
之中,RAISING
區段裡有先宣告好cx_abap_invalid_value
這個例外變數。
當一個方法具有回傳值(returning parameter),可以稱為函數式方法。不能在同一個函數式中定義多個回傳值,且需強制回傳任意值。
雖然函數式可以搭配多種參數,但多半只與導入參數(import method)一起使用,因為加入導出參數與變化參數會失去其最大優勢--直接在其他ABAP表達式中使用函數式的回傳結果。
*方法定義
METHODS get_output
RETURNING VALUE(r_output) TYPE string_table.
* 範例一
DATA(result) = connection->get_output( ).
* 範例二
out->write( data = connection->get_output()
name = ` ` ).
在上例中,可以看到兩種透過表達式使用函數方法的範例:第一個範例是直接將回傳值賦值給變數,第二個範例則是直接把回傳值作為另一個方法的輸入值(範例中為write方法)。
這篇因為各種程式碼範例,實在是有夠長...接下來要講到如何封裝方法了!