martes, 4 de julio de 2017

Un resumen de Scala for the Impatient, parte 24

Campos Abstractos

En Scala puede haber campos abstractos, y este campo abstracto es un campo sin valor inicial.

abstract class Person {
    val id: Int //no esta inicializado, por lo que es abstracto igual que su getter y setter
    var name: String //también abstracto
}

Esta clase si la vemos desde java no va tener estos atributos y los métodos getters y setters van a ser abstractos.

Dada la siguiente clase si queremos extenderla debemos definir estos atributos, por ejemplo:

class Employee(val id: Int) extends Person { // una subclase concreta
     var name = "" // propiedad concreta
}

Como se puede ver no es necesario la palabra clave “override” para sobrescribir campos.

A la vez se puede utilizar esta clase abstracta por medio de la instanciación de una clase anónima, la cual debe establecer los valores :

val fred = new Person {
     val id = 1729
     var name = "Fred"
}

Construcción ordenada y definición temprana. 

Cuando se desea sobrescribir un campo y se usa este campo en el constructor de la superclase, el resultado es un comportamiento poco intuitivo.

Veamos un ejemplo, supongamos que queremos modelar una criatura puede ver una dimensión y solo 10 unidades de medida:
   
class Creature {
    val range: Int = 10
    val env: Array[Int] = new Array[Int](range)
}

Pero una hormiga puede ver solo 2 :

class Ant extends Creature {
    override val range = 2
}

Desafortunadamente ahora tenemos un problema, el rango es un valor que es utilizado en el constructor de la superclase y el constructor de la superclase se ejecuta antes del constructor de la subclase. Vamos que sucede en mayor detalle:

  • Se llama al constructor de Ant
  • El constructor de Ant llama al constructor de criatura 
  • El constructor de criatura inicializa range en 10 y luego inicializa env con el método getter de range que fue sobrescrito, este no utiliza el de criatura sino el de hormiga que aun no fue inicializado. Por lo tanto env es un vector de 0, porque 0 es el entero por defecto. 
  • Hormiga establece a range en 2. 


Como se puede ver env se inicializo como un vector de 0 y debía ser de 2.

Existen diferentes soluciones a este problema pero podemos nombrar 3:

  • Declarar val como final. Esto es seguro pero no flexible. 
  • Declarar val como lazy, esto es seguro, flexible pero un poco ineficiente. 
  • Declarar este campo como “early definition” o  definición temprana en la subclase. 

En esta sección vamos a explorar la ultima opción. Para definir un campo con definición temprana lo podemos hacer de la siguiente manera:

class Bug extends {
     override val range = 3
} with Creature

Note que with es una palabra reservada, que se encuentra antes de la superclase.