s2 = "Hola Mundo" // s2 puede ser reasignado posteriormente El tipo de s1 y s2 ha sido inferido! Aunque a veces hay que tener cuidado: val l1 = 1 // Queria un Long pero ha inferido un Int val l2 = 1L // Long! 13 / 54
s2 = "Hola Mundo" // s2 puede ser reasignado posteriormente El tipo de s1 y s2 ha sido inferido! Aunque a veces hay que tener cuidado: val l1 = 1 // Queria un Long pero ha inferido un Int val l2 = 1L // Long! Siempre se puede explicitar el tipo: val l1: Long = 1 14 / 54
con la palabra reservada def No tiene return def f(param1: Int, param2: String): Int = { 1 } El tipo de retorno puede ser inferido def f(param1: Int, param2: String) = { 1 } 16 / 54
con la palabra reservada def No tiene return def f(param1: Int, param2: String): Int = { 1 } El tipo de retorno puede ser inferido def f(param1: Int, param2: String) = { 1 } Las llaves no son necesarias si el cuerpo de la función es una única expresión def f(param1: Int, param2: String) = 1 17 / 54
devuelve un valor que puede ser asignado a una variable val i = 2 val v = if (i % 2 == 0) "par" else "impar" v será de tipo String, si el bloque if devuelve un tipo distinto al bloque else, el tipo inferido será el primer tipo padre común val i = 2 val v = if (i % 2 == 0) "par" else 1 // v es de tipo Any 19 / 54
y setters Puedes indicar con val y var en la definición de clase si tus atributos tienen o no getters y setters // con el identificador val, los atributos pueden ser leidos desde fuera class Person(val name: String) val p1 = new Person("John Doe") p1.name // "John Doe" p1.name = "Otro nombre" // Error! 23 / 54
y setters Puedes indicar con val y var en la definición de clase si tus atributos tienen o no getters y setters // con el identificador val, los atributos pueden ser leidos desde fuera class Person(val name: String) val p1 = new Person("John Doe") p1.name // "John Doe" p1.name = "Otro nombre" // Error! // con el identificador var los atributos pueden ser leidos y escritos desde fuera class Person(var name: String) val p1 = new Person("John Doe") p1.name // "John Doe" p1.name = "Otro nombre" p1.name // "Otro Nombre" 24 / 54
b = 1 protected val c = 2 val d = 7 def method1(): Int = { ??? } } Se puede definir otros constructores class B(a: Int, b: Int) { def this(a: Int) = this(a, 0) } 25 / 54
declarar métodos Además, aquellos métodos que reciben un solo parámetro se pueden usar como operador o notación infija. class A(a: Int) { def +(that: A) = new A(this.a + this.a) } val a1 = new A(1) val a2 = new A(2) val a3 = a1 + a2 // a1.+(a2) 26 / 54
declarar métodos Además, aquellos métodos que reciben un solo parámetro se pueden usar como operador o notación infija. class A(a: Int) { def +(that: A) = new A(this.a + this.a) } val a1 = new A(1) val a2 = new A(2) val a3 = a1 + a2 // a1.+(a2) En scala Int es un objeto, así que: 1 + 2 == 1.+(2) // true!! 27 / 54
un "singleton" package mypackage object Matematicas { def sum(a: Int, b: Int): Int = a + b } Muchas veces es solo un "namespace". sum puede ser accedido con mypackage.Matematicas.sum 28 / 54
mismo nombre que la clase Su uso más tipico es el de "factory" El companion object es donde definiríamos los métodos "static" class Person(name: String) object Person { def apply(name: String) = new Person(name) } 29 / 54
mismo nombre que la clase Su uso más tipico es el de "factory" El companion object es donde definiríamos los métodos "static" class Person(name: String) object Person { def apply(name: String) = new Person(name) } Person es tambien un singleton ahora, así que podemos crear objetos Person sin el uso de new val p1 = Person("John Doe") val p2 = Person.apply("John Doe") 30 / 54
a los métodos trait Printable { // Tiene que ser implementado def format(): String // Implementación por defecto def print(): Unit = println(this.format) } 31 / 54
a los métodos trait Printable { // Tiene que ser implementado def format(): String // Implementación por defecto def print(): Unit = println(this.format) } Si en una clase dejas un método sin implementar tienes que denotarla como abstract abstract class Exec { def run(): Unit } 32 / 54
de una clase. class A class B extends A Pero puede tener más de un "mix-in". class A trait Executable trait Printable class B extends A with Executable with Printable 34 / 54
tienen el identificador val por defecto Por defecto tiene los métodos toString, hashCode y equals implementados Los métodos apply y unapply también implementados. 36 / 54
persona val p1 = Person("John Doe") // Se puede acceder a sus atributos println(p1.name) //John Doe // Automaticamente genera toString println(p1) //Person(John Doe) // tenemos método equals así que comparar es muy sencillo println(p1 == Person("John Doe")) // true 37 / 54
case 2014 => "2014" case 2015 => "2015" case _ => "Otro" // _ actúa como wildcard } Variables: year match { case 2014 => "2014" case 2015 => "2015" case y => s"Otro: $y" } 40 / 54
case 2014 => "2014" case 2015 => "2015" case _ => "Otro" // _ actúa como wildcard } Variables: year match { case 2014 => "2014" case 2015 => "2015" case y => s"Otro: $y" } Incluso se puede poner condiciones: year match { case y if (y == 2014 || y == 2015) => y.toString case y => s"Otro: $y" } 41 / 54
val p1 = Person("John Doe", 43) p1 match { case Person("John Doe", 40) => "Match en todos los valores del constructor" case Person("Juan Nadie", a) => "Match por nombre, mientras que la edad es asignada a u case Person(n, a) => s"Persona cuyo nombre es $n y edad $a" case p @ Person(n, a) => p.toString } 42 / 54
función como parámetro o devuelven una función. En scala las funciones son "ciudadanos de primer order". Es decir, podemos especificar que nuestra expresión devuelve una función o asignar una función a una variable. val f1: Int => Int = (x: Int) => x + 1 val f2: Int => Int = x => x + 1 val f3: Int => Int = _ + 1 45 / 54