latest

Introducción al lenguaje Dart. Intermedio. Parte 2

Tras estudiar las peculiaridades de la definición de clases y constructores en Dart en la parte 1 de esta Introducción al lenguaje Dart, nivel Intermedio. Continuamos con este lenguaje de programación que usaremos a lo largo del curso de Flutter.  

La implementación de herencia y el polimorfismo en Dart no difiere de cómo se hace en Java. Tan solo hay algunos cambios a nivel de sintaxis del lenguaje. El ejemplo de la parte inferior de esta página trata de ilustrar los aspectos principales de la herencia (por ahora herencia simple) y el polimorfismo en Dart.

En el ejemplo podemos ver una clase padre Empleado con un único atributo _nombre. También se define explícitamente un constructor de forma reducida como hemos visto en otras ocasiones. Importante remarcar que al anteceder el nombre del atributo con un guión bajo estamos modificando sus propiedades de acceso para que sólo los miembros de su misma clase, así como los miembros de las subclases tengan acceso a él.

Siguiendo con la definición de la clase Empleado, Dart permite simplificar la implementación de los métodos getters y setters para las propiedades de una clase con una sintaxis reducida como la que podéis ver en el ejemplo. Por último, la clase padre Empleado sobreescribe el método toString(), se añade la notificación @override para enfatizar que es un método sobreescrito.

Tras la definición de la clase padre se implementa una clase hijo que extiende de ella denominada Operario. Como puede comprobarse, la palabra clave extends es utilizada de la misma manera que en Java para indicar que Operario es una subclase de Empleado.

La subclase Operario define una propiedad _altoRiesgoLaboral que sólo tendrán los operarios y que indica si el sujeto en cuestión utiliza (o no) en su trabajo alguna herramienta que entrañe riesgo para su seguridad. Se define un constructor explícitamente con dos atributos (_nombre,_altoRiesgoLaboral). Prestad atención a como se emplea la palabra clave super para hacer referencia al constructor definido explícitamente en la clase padre (Empleado(String nombre)). Esto garantiza que primero se ejecutará el código del constructor de la clase padre (inicializando las propiedades comunes); y luego el código del constructor de Operario.

Continuando con la definición de la clase Operario en el ejemplo inferior, se implementan los métodos getters y setters para la propiedad _altoRiesgoLaboral y, por último, se sobreescribe el método toString(). Observad como se usa aquí la palabra clave super para invocar al método toString() de la clase padre (de no hacerlo así causaría un bucle infinito).

El código del ejemplo termina con la definición de otra subclase de Empleado denominada Directivo. No entraremos más en detalle en esta subclase dado que se implementa de manera similar a como se hace con Operario.

void main(){
  	var persona1 = new Empleado("Juan");
  	print(persona1);
  	// La siguiente sentencia provocaría un error de compilación
  	// print(persona1.altoRiesgoLaboral);
  
  	Operario persona2 = new Operario("Alicia",true);
  	print("\n$persona2");
  	print(persona2.altoRiesgoLaboral);
  
  	var persona3 = new Operario("Rafael",false);
  	print("\n$persona3");
  	print(persona3.altoRiesgoLaboral);
  
  	Empleado persona4 = new Operario("Luis",true);
  	print("\n$persona4");
  	// La siguiente sentencia provocaría un error de compilación
  	// print(persona4.altoRiesgoLaboral);
  	print( (persona4 as Operario).altoRiesgoLaboral );
  
  	Empleado persona5 = new Directivo("Manuel");
  	print("\n$persona5");
}
class Empleado{
	// Propiedades comunes de un empleado...
  	// El guíón bajo es un modificador de acceso 
  	// (indica que es una propiedad privada) 
  	String _nombre;
  
  	// Constructor
  	Empleado(this._nombre);
  	
  	// Métodos
  	//Getters & Setters
  	String get nombre => _nombre;
  	set nombre(String nombre) => _nombre = nombre;
  
  	@override  	
  	String toString(){
      return "Soy $_nombre y soy un empleado";
    }
}
class Operario extends Empleado{
  	bool _altoRiesgoLaboral;
  
  	// Constructor
  	Operario(String _nombre,this._altoRiesgoLaboral) : super(_nombre);
    
    // Métodos
  	// Getters & Setters
  	bool get altoRiesgoLaboral => _altoRiesgoLaboral;
  	set altoRiesgoLaboral(bool altoRiesgoLaboral) => 
      _altoRiesgoLaboral = altoRiesgoLaboral;
  
  	@override
  	String toString(){
      String riesgo = _altoRiesgoLaboral == true ? 
        "uso herramientas peligrosa": "NO uso herramientas peligrosa";
      return super.toString() + " de tipo operario y " + riesgo;
    }
}
class Directivo extends Empleado{
	// Propiedades de un directivo...
    
  	// Constructor
  	Directivo(String _nombre) : super(_nombre);
  
	// Métodos  
  	@override
  	String toString(){
      return super.toString() + " de tipo directivo";
    }
}

La salida del método main de este ejemplo puede verse a continuación:

Soy Juan y soy un empleado

Soy Alicia y soy un empleado de tipo operario y uso herramientas peligrosa
true

Soy Rafael y soy un empleado de tipo operario y NO uso herramientas peligrosa
false

Soy Luis y soy un empleado de tipo operario y uso herramientas peligrosa
true

Soy Manuel y soy un empleado de tipo directivo

Se recomienda encarecidamente que probéis el ejemplo en DartPad y comprendáis que el comportamiento polimórfico en Dart es exactamente igual a como cabría esperar en Java.

La siguiente lección tratará sobre conceptos avanzados de Dart que también están relacionados con la POO, su propuesta de implementación de herencia múltiple, clases abstractas, etc.

Author image
Iván González is postdoctoral researcher at the Castilla-La Mancha University.
Ciudad Real (Spain)