La herencia es un concepto central de POO que permite la reutilizacion y extension de clases. En ABAP, la herencia se realiza con INHERITING FROM. ABSTRACT y FINAL controlan la extensibilidad.
Conceptos basicos
- Herencia: Una clase (subclase) adopta propiedades de otra (clase base)
- ABSTRACT: Clases/metodos que no pueden instanciarse/llamarse directamente
- FINAL: Clases/metodos que no pueden heredarse/sobrescribirse mas
- REDEFINITION: Sobrescribir metodos de la clase base en la subclase
Sintaxis
Herencia
CLASS <subclase> DEFINITION INHERITING FROM <clase_base>.Abstract
CLASS <nombre_clase> DEFINITION ABSTRACT. METHODS: <nombre_metodo> ABSTRACT.ENDCLASS.Final
CLASS <nombre_clase> DEFINITION FINAL. METHODS: <nombre_metodo> FINAL.ENDCLASS.Redefinition
CLASS <subclase> DEFINITION INHERITING FROM <clase_base>. METHODS: <nombre_metodo> REDEFINITION.ENDCLASS.Ejemplos
1. Herencia simple
" Clase baseCLASS lcl_vehicle DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string, get_brand RETURNING VALUE(rv_brand) TYPE string, start.
PROTECTED SECTION. DATA: mv_brand TYPE string.ENDCLASS.
CLASS lcl_vehicle IMPLEMENTATION. METHOD constructor. mv_brand = iv_brand. ENDMETHOD.
METHOD get_brand. rv_brand = mv_brand. ENDMETHOD.
METHOD start. WRITE: / 'Vehiculo arrancando...'. ENDMETHOD.ENDCLASS.
" SubclaseCLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i, get_doors RETURNING VALUE(rv_doors) TYPE i.
PRIVATE SECTION. DATA: mv_doors TYPE i.ENDCLASS.
CLASS lcl_car IMPLEMENTATION. METHOD constructor. " Llamar constructor de clase base super->constructor( iv_brand ). mv_doors = iv_doors. ENDMETHOD.
METHOD get_doors. rv_doors = mv_doors. ENDMETHOD.ENDCLASS.
" UsoDATA: lo_car TYPE REF TO lcl_car.
lo_car = NEW #( iv_brand = 'BMW' iv_doors = 4 ).
WRITE: / lo_car->get_brand( ). " BMW (heredado)WRITE: / lo_car->get_doors( ). " 4 (metodo propio)lo_car->start( ). " Vehiculo arrancando... (heredado)2. Sobrescribir metodos (REDEFINITION)
CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i, start REDEFINITION. " Sobrescribir metodo
PRIVATE SECTION. DATA: mv_doors TYPE i.ENDCLASS.
CLASS lcl_car IMPLEMENTATION. METHOD constructor. super->constructor( iv_brand ). mv_doors = iv_doors. ENDMETHOD.
METHOD start. " Implementacion propia WRITE: / 'Auto arranca el motor...'.
" Opcional: Llamar metodo de clase base " super->start( ). ENDMETHOD.ENDCLASS.
" UsoDATA: lo_vehicle TYPE REF TO lcl_vehicle, lo_car TYPE REF TO lcl_car.
lo_vehicle = NEW lcl_vehicle( 'Generic' ).lo_vehicle->start( ). " Vehiculo arrancando...
lo_car = NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ).lo_car->start( ). " Auto arranca el motor...3. SUPER - Llamar clase base
CLASS lcl_electric_car DEFINITION INHERITING FROM lcl_car. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i iv_battery TYPE i, start REDEFINITION.
PRIVATE SECTION. DATA: mv_battery_capacity TYPE i.ENDCLASS.
CLASS lcl_electric_car IMPLEMENTATION. METHOD constructor. " Llamar constructor de clase padre super->constructor( iv_brand = iv_brand iv_doors = iv_doors ). mv_battery_capacity = iv_battery. ENDMETHOD.
METHOD start. WRITE: / 'Verificacion de bateria...'. WRITE: / |Capacidad: { mv_battery_capacity } kWh|.
" Llamar metodo de clase base super->start( ). ENDMETHOD.ENDCLASS.
" UsoDATA(lo_tesla) = NEW lcl_electric_car( iv_brand = 'Tesla' iv_doors = 4 iv_battery = 100).
lo_tesla->start( )." Salida:" Verificacion de bateria..." Capacidad: 100 kWh" Auto arranca el motor...4. Clases abstractas
" Clase abstracta - no puede instanciarseCLASS lcl_shape DEFINITION ABSTRACT. PUBLIC SECTION. " Metodo abstracto - DEBE implementarse en subclase METHODS: calculate_area ABSTRACT RETURNING VALUE(rv_area) TYPE f.
" Metodo concreto - puede heredarse METHODS: get_name RETURNING VALUE(rv_name) TYPE string.
PROTECTED SECTION. DATA: mv_name TYPE string.ENDCLASS.
CLASS lcl_shape IMPLEMENTATION. METHOD get_name. rv_name = mv_name. ENDMETHOD. " calculate_area no tiene implementacion (abstracto)ENDCLASS.
" Subclase concreta: CirculoCLASS lcl_circle DEFINITION INHERITING FROM lcl_shape. PUBLIC SECTION. METHODS: constructor IMPORTING iv_radius TYPE f, calculate_area REDEFINITION.
PRIVATE SECTION. DATA: mv_radius TYPE f. CONSTANTS: c_pi TYPE f VALUE '3.14159265'.ENDCLASS.
CLASS lcl_circle IMPLEMENTATION. METHOD constructor. super->constructor( ). mv_name = 'Circulo'. mv_radius = iv_radius. ENDMETHOD.
METHOD calculate_area. rv_area = c_pi * mv_radius ** 2. ENDMETHOD.ENDCLASS.
" Subclase concreta: RectanguloCLASS lcl_rectangle DEFINITION INHERITING FROM lcl_shape. PUBLIC SECTION. METHODS: constructor IMPORTING iv_width TYPE f iv_height TYPE f, calculate_area REDEFINITION.
PRIVATE SECTION. DATA: mv_width TYPE f, mv_height TYPE f.ENDCLASS.
CLASS lcl_rectangle IMPLEMENTATION. METHOD constructor. super->constructor( ). mv_name = 'Rectangulo'. mv_width = iv_width. mv_height = iv_height. ENDMETHOD.
METHOD calculate_area. rv_area = mv_width * mv_height. ENDMETHOD.ENDCLASS.
" UsoDATA: lt_shapes TYPE TABLE OF REF TO lcl_shape.
" lo_shape = NEW lcl_shape( ). " ERROR! Es abstracta!
APPEND NEW lcl_circle( 5 ) TO lt_shapes.APPEND NEW lcl_rectangle( iv_width = 4 iv_height = 3 ) TO lt_shapes.
LOOP AT lt_shapes INTO DATA(lo_shape). WRITE: / lo_shape->get_name( ), ': Area =', lo_shape->calculate_area( ).ENDLOOP.
" Salida:" Circulo: Area = 78.54" Rectangulo: Area = 12.005. FINAL - Prevenir herencia
" Clase final - NO puede heredarseCLASS lcl_singleton DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO lcl_singleton.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO lcl_singleton. METHODS: constructor.ENDCLASS.
CLASS lcl_singleton IMPLEMENTATION. METHOD constructor. " Constructor privado ENDMETHOD.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD.ENDCLASS.
" Esta linea causaria error de sintaxis:" CLASS lcl_child DEFINITION INHERITING FROM lcl_singleton." ..." ENDCLASS.6. Metodos FINAL
CLASS lcl_base DEFINITION. PUBLIC SECTION. " Este metodo NO puede sobrescribirse METHODS: critical_operation FINAL.
" Este metodo puede sobrescribirse METHODS: normal_operation.ENDCLASS.
CLASS lcl_base IMPLEMENTATION. METHOD critical_operation. WRITE: / 'Operacion critica - no modificable'. ENDMETHOD.
METHOD normal_operation. WRITE: / 'Operacion normal'. ENDMETHOD.ENDCLASS.
CLASS lcl_derived DEFINITION INHERITING FROM lcl_base. PUBLIC SECTION. " OK: normal_operation puede redefinirse METHODS: normal_operation REDEFINITION.
" ERROR: critical_operation es FINAL " METHODS: critical_operation REDEFINITION.ENDCLASS.
CLASS lcl_derived IMPLEMENTATION. METHOD normal_operation. WRITE: / 'Operacion sobrescrita'. ENDMETHOD.ENDCLASS.7. PROTECTED - Acceso para subclases
CLASS lcl_account DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_balance TYPE p, get_balance RETURNING VALUE(rv_balance) TYPE p, deposit IMPORTING iv_amount TYPE p.
PROTECTED SECTION. " Solo visible para esta clase y subclases DATA: mv_balance TYPE p DECIMALS 2. METHODS: validate_amount IMPORTING iv_amount TYPE p RETURNING VALUE(rv_valid) TYPE abap_bool.
PRIVATE SECTION. " Solo visible para esta clase DATA: mv_account_number TYPE string.ENDCLASS.
CLASS lcl_account IMPLEMENTATION. METHOD constructor. mv_balance = iv_balance. ENDMETHOD.
METHOD get_balance. rv_balance = mv_balance. ENDMETHOD.
METHOD deposit. IF validate_amount( iv_amount ). mv_balance = mv_balance + iv_amount. ENDIF. ENDMETHOD.
METHOD validate_amount. rv_valid = xsdbool( iv_amount > 0 ). ENDMETHOD.ENDCLASS.
CLASS lcl_savings_account DEFINITION INHERITING FROM lcl_account. PUBLIC SECTION. METHODS: add_interest IMPORTING iv_rate TYPE p.ENDCLASS.
CLASS lcl_savings_account IMPLEMENTATION. METHOD add_interest. " Acceso a PROTECTED mv_balance posible DATA(lv_interest) = mv_balance * iv_rate / 100.
" Acceso a PROTECTED validate_amount posible IF validate_amount( lv_interest ). mv_balance = mv_balance + lv_interest. ENDIF.
" ERROR: mv_account_number es PRIVATE " mv_account_number = '123'. ENDMETHOD.ENDCLASS.8. Polimorfismo
" Diferentes tipos de vehiculosCLASS lcl_motorcycle DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: start REDEFINITION.ENDCLASS.
CLASS lcl_motorcycle IMPLEMENTATION. METHOD start. WRITE: / 'Moto arranca rugiendo...'. ENDMETHOD.ENDCLASS.
CLASS lcl_truck DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: start REDEFINITION.ENDCLASS.
CLASS lcl_truck IMPLEMENTATION. METHOD start. WRITE: / 'Diesel de camion arranca...'. ENDMETHOD.ENDCLASS.
" Uso polimorficoDATA: lt_fleet TYPE TABLE OF REF TO lcl_vehicle.
APPEND NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ) TO lt_fleet.APPEND NEW lcl_motorcycle( 'Honda' ) TO lt_fleet.APPEND NEW lcl_truck( 'MAN' ) TO lt_fleet.
" Todos los vehiculos arrancan - cada uno a su maneraLOOP AT lt_fleet INTO DATA(lo_vehicle). lo_vehicle->start( ).ENDLOOP.
" Salida:" Auto arranca el motor..." Moto arranca rugiendo..." Diesel de camion arranca...9. Verificacion de tipo con IS INSTANCE OF
DATA: lo_vehicle TYPE REF TO lcl_vehicle.
lo_vehicle = NEW lcl_car( iv_brand = 'Audi' iv_doors = 4 ).
" Verificacion de tipoIF lo_vehicle IS INSTANCE OF lcl_car. WRITE: / 'Es un auto'.
" Downcast para metodos especificos DATA(lo_car) = CAST lcl_car( lo_vehicle ). WRITE: / 'Puertas:', lo_car->get_doors( ).ENDIF.
" Con CASE TYPE OFCASE TYPE OF lo_vehicle. WHEN TYPE lcl_car. WRITE: / 'Auto reconocido'. WHEN TYPE lcl_motorcycle. WRITE: / 'Moto reconocida'. WHEN TYPE lcl_truck. WRITE: / 'Camion reconocido'. WHEN OTHERS. WRITE: / 'Tipo de vehiculo desconocido'.ENDCASE.10. Clase abstracta con patron Template Method
CLASS lcl_report DEFINITION ABSTRACT. PUBLIC SECTION. " Template Method - define el flujo METHODS: execute FINAL.
PROTECTED SECTION. " Hook Methods - a implementar por subclases METHODS: fetch_data ABSTRACT, process_data ABSTRACT, display_output ABSTRACT.ENDCLASS.
CLASS lcl_report IMPLEMENTATION. METHOD execute. " Flujo fijo, no sobrescribible (FINAL) WRITE: / 'Reporte inicia...'. fetch_data( ). process_data( ). display_output( ). WRITE: / 'Reporte finaliza.'. ENDMETHOD.ENDCLASS.
CLASS lcl_sales_report DEFINITION INHERITING FROM lcl_report. PROTECTED SECTION. METHODS: fetch_data REDEFINITION, process_data REDEFINITION, display_output REDEFINITION.ENDCLASS.
CLASS lcl_sales_report IMPLEMENTATION. METHOD fetch_data. WRITE: / ' Cargando datos de ventas...'. ENDMETHOD.
METHOD process_data. WRITE: / ' Calculando ingresos...'. ENDMETHOD.
METHOD display_output. WRITE: / ' Mostrando reporte de ventas.'. ENDMETHOD.ENDCLASS.
" UsoDATA(lo_report) = NEW lcl_sales_report( ).lo_report->execute( ).
" Salida:" Reporte inicia..." Cargando datos de ventas..." Calculando ingresos..." Mostrando reporte de ventas." Reporte finaliza.Jerarquia de herencia
lcl_vehicle (Clase base) │ ├── lcl_car │ │ │ └── lcl_electric_car │ ├── lcl_motorcycle │ └── lcl_truckResumen
| Concepto | Palabra clave | Descripcion |
|---|---|---|
| Herencia | INHERITING FROM | Clase extiende otra clase |
| Sobrescribir | REDEFINITION | Reimplementar metodo de clase base |
| Llamar clase base | super-> | Acceso a clase padre |
| Abstracto | ABSTRACT | No instanciable / debe implementarse |
| Final | FINAL | No heredable / no sobrescribible |
| Protegido | PROTECTED | Visible para clase y subclases |
Notas importantes / Mejores practicas
- Herencia para relaciones “es-un” (Auto es un Vehiculo).
- Para relaciones “tiene-un” mejor usar composicion.
PROTECTEDpara atributos/metodos que subclases necesitan.ABSTRACTpara clases base que solo sirven como plantilla.FINALpara clases/metodos que no deben modificarse.- Polimorfismo permite tratamiento uniforme de diferentes tipos.
super->para llamar implementacion de clase base.- Verificar tipos con
IS INSTANCE OFantes deCAST. - Preferir
INTERFACEpara acoplamiento debil. - Evitar jerarquias de herencia profundas (max. 2-3 niveles).