sábado, 4 de marzo de 2017

Un resumen de Scala for the Impatient, parte 12

Zipping

Una de las razones para utilizar tuplas, es porque podemos procesar un conjunto de elementos de distinto tipo, todos juntos. Esto es común cuando utilizamos el método zip:

val symbols = Array("<", "-", ">")
val counts = Array(2, 10, 2)
val pairs = symbols.zip(counts)

y esto retornara pares de la siguiente forma:

Array(("<", 2), ("-", 10), (">", 2))

Luego estos pares se pueden procesar juntos:

for ((s, n) <- pairs) Console.print(s * n) // Prints <<---------->>

Algo que podemos hacer es luego pasar este par a un mapa con el método toMap :

keys.zip(values).toMap

Clases

Es hora de hablar de clases, para el que no esta muy interiorizado con la programación orientada a objetos, le recomiendo que lea algo de teoría de objetos antes de seguir.

Scala cuenta con una estructura para definir clases como java o c++. Pero un tanto más moderna y nos simplifica bastante las escritura.

class Counter {
  private var value = 0 // Se deben inicializar los valores
  def increment() { value += 1 } // Métodos por defecto son públicos
  def current() = value
}

En Scala no es necesario indicar que una clase es publicas. Un archivo puede contener muchas clases y estas son todas publicas.

Si queremos utilizar la clase declarada lo podemos hacer:

val myCounter = new Counter // o new Counter()
myCounter.increment()
println(myCounter.current)

Se pueden llamar los métodos sin parámetros con paréntesis o sin paréntesis:

myCounter.current // OK
myCounter.current() // también esta bien

Cual es la mejor forma? Por conversión se indica que se utilicen los paréntesis cuando el método modifica un valor, si es un simple método para acceder a un valor no es necesario. Como lo usamos anteriormente:

myCounter.increment() //modifica el valor
println(myCounter.current) // solo accede

Se puede forzar a no utilizar parentesis en los metodos de la siguiente manera:

class Counter {
def current = value // El metodo no es definido con ()
}

Ahora el método current debe ser usado sin parentesis

Propiedades getter y setters

En Java necesitamos declarar métodos que nos permitan acceder a nuestros atributos privados:

public class Person { // Esto es Java
  private int age = 0;
  public int getAge() { return age; }
  public void setAge(int age) { this.age = age; }
}

Porque es mejor esto que hacer publico age? Porque podemos indicar restricciones, por ejemplo si no queremos que la persona pueda ser más joven:

public void setAge(int newValue) { if (newValue > age) age = newValue; }

Como se puede ver es mucho mejor usar métodos getters y setters que hacer públicos los atributos de una clase.

Scala provee métodos getters y setters para los campos, aquí podemos ver una definición de un campo publico:

class Person {
  var age = 0
}

Scala genera una clase para la JVM con un atributo privado y métodos getter y setter públicos, si declaramos este atributo privado va a generar métodos getter y setter privados.

En scala los métodos son llamados age y age_= , de esta manera si hacemos:

println(fred.age) // Llama al metodo fred.age()
fred.age = 21 // Llama al metodo fred.age_=(21)

Esto es mucho mejor que tener atributos privados, porque podemos redefinir estos métodos:

class Person {
private var privateAge = 0 // hacemos privado a la edad y la renombramos.

def age = privateAge
def age_=(newValue: Int) {
if (newValue > privateAge) privateAge = newValue; // No puede ser más joven
}

Ahora podemos seguir accediendo a age pero no podemos rejuvenecer a una persona:

val fred = new Person
fred.age = 30
fred.age = 21
println(fred.age) // 30

Notemos que en Scala no tenemos la necesidad de saber si estamos accediendo a un método o a un atributo, de esta forma scala puede implementar este mecanismo.

Si esperamos tener una propiedad solo lectura, es decir que no se pueda modificar podemos utilizar la palabra val al declarar este atributo:

class Message {
  val timeStamp = new java.util.Date
  …
}

Como ya vimos anteriormente, en ciertos casos es necesario mantener un atributo que no sea modificable directamente:

class Counter {
   private var value = 0 // Se deben inicializar los valores
   def increment() { value += 1 } // Métodos por defecto son públicos
   def current = value
}