ABAP Referencias de datos: REF, GET REFERENCE y desreferenciacion

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Las referencias de datos en ABAP son punteros tipados que apuntan a objetos de datos en memoria. A diferencia de los Field-Symbols, que apuntan directamente a memoria, las referencias de datos son variables independientes que almacenan una direccion.

Concepto basico

  • Una referencia de datos es una variable que contiene la direccion de memoria de un objeto de datos
  • Puede ser pasada, almacenada y comparada
  • El acceso a los datos se realiza mediante desreferenciacion (->*)
  • Las referencias de datos pueden estar iniciales (NULL)

Sintaxis

Declarar referencia de datos

" Referencia tipada
DATA: lr_data TYPE REF TO <tipo_datos>.
" Referencia generica (a cualquier dato)
DATA: lr_any TYPE REF TO data.
" Declaracion inline
DATA(lr_inline) = REF #( variable ).

Crear referencia

" Moderno (desde 7.40): REF #()
lr_data = REF #( variable ).
" Clasico: GET REFERENCE
GET REFERENCE OF variable INTO lr_data.
" Crear nuevo objeto de datos
CREATE DATA lr_data TYPE <tipo>.
lr_data = NEW #( ). " Desde 7.40

Desreferenciacion

" Acceso a los datos
<valor> = lr_data->*.
" En estructuras: acceso a componentes
<valor> = lr_struct->comp_name.
" En tablas: acceso a filas
LOOP AT lr_table->* INTO DATA(ls_line).
...
ENDLOOP.

Campos del sistema

Despues de operaciones con referencias de datos:

  • Referencia IS BOUND: Apunta a datos validos
  • Referencia IS INITIAL: No apunta a nada (NULL)

Ejemplos

1. Uso basico

DATA: lv_number TYPE i VALUE 42.
DATA: lr_number TYPE REF TO i.
" Crear referencia a variable
lr_number = REF #( lv_number ).
" Desreferenciacion: leer valor
WRITE: / 'Valor:', lr_number->*. " 42
" Desreferenciacion: cambiar valor
lr_number->* = 100.
WRITE: / 'Original:', lv_number. " 100 (cambiado!)

2. Verificar referencia

DATA: lr_data TYPE REF TO string.
" Verificar si la referencia es valida
IF lr_data IS BOUND.
WRITE: / 'La referencia apunta a:', lr_data->*.
ELSE.
WRITE: / 'La referencia es NULL'.
ENDIF.
IF lr_data IS INITIAL.
WRITE: / 'Referencia no inicializada'.
ENDIF.
" Liberar referencia
FREE lr_data.

3. GET REFERENCE (sintaxis clasica)

DATA: lv_text TYPE string VALUE 'Hola Mundo'.
DATA: lr_text TYPE REF TO string.
" Sintaxis clasica
GET REFERENCE OF lv_text INTO lr_text.
WRITE: / lr_text->*. " Hola Mundo

4. Crear nuevo objeto de datos

DATA: lr_number TYPE REF TO i.
" Clasico: CREATE DATA
CREATE DATA lr_number.
lr_number->* = 42.
" Moderno: NEW #()
DATA(lr_string) = NEW string( 'Nuevo String' ).
WRITE: / lr_string->*.
" Con tipo
DATA: lr_any TYPE REF TO data.
CREATE DATA lr_any TYPE i.
" Generico con tipo dinamico
DATA: lv_typename TYPE string VALUE 'STRING'.
CREATE DATA lr_any TYPE (lv_typename).

5. Referencias con tablas internas

TYPES: BEGIN OF ty_customer,
id TYPE i,
name TYPE string,
END OF ty_customer.
DATA: lt_customers TYPE TABLE OF ty_customer,
lr_table TYPE REF TO ty_customer.
lt_customers = VALUE #(
( id = 1 name = 'Muller' )
( id = 2 name = 'Schmidt' )
).
" REFERENCE INTO en el LOOP
LOOP AT lt_customers REFERENCE INTO lr_table.
WRITE: / lr_table->id, lr_table->name.
" Cambio via referencia
lr_table->name = to_upper( lr_table->name ).
ENDLOOP.
" READ TABLE con REFERENCE INTO
READ TABLE lt_customers REFERENCE INTO lr_table
WITH KEY id = 1.
IF sy-subrc = 0.
lr_table->name = 'Cambiado'.
ENDIF.

6. Almacenar referencias en tablas

DATA: lt_refs TYPE TABLE OF REF TO string.
DATA: lv_str1 TYPE string VALUE 'Uno',
lv_str2 TYPE string VALUE 'Dos',
lv_str3 TYPE string VALUE 'Tres'.
" Recolectar referencias
APPEND REF #( lv_str1 ) TO lt_refs.
APPEND REF #( lv_str2 ) TO lt_refs.
APPEND REF #( lv_str3 ) TO lt_refs.
" Recorrer referencias
LOOP AT lt_refs INTO DATA(lr_str).
WRITE: / lr_str->*.
ENDLOOP.
" Cambiar valores originales via referencias
READ TABLE lt_refs INTO lr_str INDEX 1.
lr_str->* = 'CAMBIADO'.
WRITE: / lv_str1. " CAMBIADO

7. Pasar referencias a metodos

CLASS lcl_processor DEFINITION.
PUBLIC SECTION.
METHODS: process_data IMPORTING ir_data TYPE REF TO i.
ENDCLASS.
CLASS lcl_processor IMPLEMENTATION.
METHOD process_data.
IF ir_data IS BOUND.
ir_data->* = ir_data->* * 2.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Uso
DATA: lv_value TYPE i VALUE 50,
lo_proc TYPE REF TO lcl_processor.
lo_proc = NEW #( ).
lo_proc->process_data( REF #( lv_value ) ).
WRITE: / lv_value. " 100

8. Referencias genericas (REF TO data)

DATA: lr_any TYPE REF TO data.
FIELD-SYMBOLS: <fs_data> TYPE any.
" Referencia a Integer
DATA(lv_int) = 42.
lr_any = REF #( lv_int ).
" Para el acceso: asignar Field-Symbol
ASSIGN lr_any->* TO <fs_data>.
WRITE: / <fs_data>.
" Referencia a String
DATA(lv_str) = |Hola|.
lr_any = REF #( lv_str ).
ASSIGN lr_any->* TO <fs_data>.
WRITE: / <fs_data>.

9. Creacion dinamica de objetos

DATA: lr_data TYPE REF TO data,
lv_type TYPE string VALUE 'STRING'.
" Crear objeto de datos dinamicamente
CREATE DATA lr_data TYPE (lv_type).
" Asignar valor via Field-Symbol
FIELD-SYMBOLS: <fs> TYPE any.
ASSIGN lr_data->* TO <fs>.
<fs> = 'Creado dinamicamente'.
WRITE: / <fs>.
" Para estructuras
DATA: lv_struct_name TYPE string VALUE 'SFLIGHT'.
CREATE DATA lr_data TYPE (lv_struct_name).
ASSIGN lr_data->* TO <fs>.

10. Comparacion de referencias

DATA: lv_data TYPE string VALUE 'Test'.
DATA: lr_ref1 TYPE REF TO string,
lr_ref2 TYPE REF TO string,
lr_ref3 TYPE REF TO string.
lr_ref1 = REF #( lv_data ).
lr_ref2 = REF #( lv_data ).
lr_ref3 = lr_ref1.
" Comparacion de referencias (apuntan a la misma direccion?)
IF lr_ref1 = lr_ref3.
WRITE: / 'ref1 y ref3 son identicas'. " Si
ENDIF.
IF lr_ref1 = lr_ref2.
WRITE: / 'ref1 y ref2 son identicas'. " No! (referencias diferentes)
ENDIF.
" Comparacion de valores
IF lr_ref1->* = lr_ref2->*.
WRITE: / 'Los valores son iguales'. " Si
ENDIF.

Referencia de datos vs. Field-Symbol

AspectoReferencia de datosField-Symbol
TipoVariable (REF TO)Marcador de posicion
AlmacenarSi, puede almacenarseNo, solo temporal
PasarPuede pasarse como parametroSolo uso local
Estado NULLPuede estar inicial/sin vincularNo asignado
Sintaxislr_data->*<fs>
RendimientoLigeramente mas lentoLigeramente mas rapido
AplicacionAlmacenar, pasarAcceso local rapido
" Field-Symbol: Acceso local rapido
LOOP AT lt_data ASSIGNING FIELD-SYMBOL(<fs>).
<fs>-field = 'X'.
ENDLOOP.
" Referencia de datos: Cuando la referencia debe almacenarse
LOOP AT lt_data REFERENCE INTO DATA(lr_line).
APPEND lr_line TO lt_references. " Almacenar referencia
ENDLOOP.

Casos de uso tipicos

1. Parametros opcionales

METHODS: process
IMPORTING ir_config TYPE REF TO ty_config OPTIONAL.
METHOD process.
IF ir_config IS BOUND.
" Usar configuracion
ELSE.
" Valores por defecto
ENDIF.
ENDMETHOD.

2. Retorno de referencias

METHODS: get_instance
RETURNING VALUE(rr_instance) TYPE REF TO lcl_singleton.
METHOD get_instance.
IF gr_instance IS NOT BOUND.
gr_instance = NEW #( ).
ENDIF.
rr_instance = gr_instance.
ENDMETHOD.

3. Estructuras de datos dinamicas

" Estructura de arbol con referencias
TYPES: BEGIN OF ty_node,
value TYPE string,
children TYPE TABLE OF REF TO ty_node WITH EMPTY KEY,
END OF ty_node.
DATA: lr_root TYPE REF TO ty_node.
lr_root = NEW #( value = 'Root' ).
APPEND NEW ty_node( value = 'Child 1' ) TO lr_root->children.

Notas importantes / Mejores practicas

  • Verifica siempre IS BOUND antes de desreferenciar.
  • Usa REF #() en lugar de GET REFERENCE en ABAP moderno.
  • Field-Symbols son mas eficientes para acceso local.
  • Referencias de datos son necesarias cuando las referencias deben almacenarse o pasarse.
  • CREATE DATA para determinacion dinamica de tipos en tiempo de ejecucion.
  • Cuidado con referencias genericas (REF TO data) - se pierde la seguridad de tipos.
  • Combina con LOOP AT ... REFERENCE INTO para acceso eficiente a tablas.
  • Usa referencias en atributos de CLASS para relaciones entre objetos.