Está en la página 1de 55

Universidad Nacional de La

Matanza
Clase 3:
Clases y Objetos

Android Development with Kotlin v1.0 This work is licensed under the Apache 2 license. 2
Agenda

Clase 3: Clases y objetos


○ Clases
○ Herencia
○ Funciones de extensión
○ Clases Especiales
○ Organizando nuestro código

3
Clases

4
Clase

● Las clases son planos para los objetos


● Las clases definen métodos que
pueden operar en sus objetos Instancias de
Objetos

Clase
5
Class versus object instance
Clase Casa
Datos

● Color (String) Instancias

● Cantidad de ventanas (Int)


● En venta (Boolean)

Behavior
● actualizarColor() EN VENTA

6
● ponerEnVenta()
Definir y usar clases
Definición de la clase Crear una nueva instancia de
Casa
class Casa {
val miCasa = Casa()
val color: String = "white"

val cantidadDeVentanas: Int = 2 println(miCasa)


val enVenta: Boolean = false

fun actualizarColor(color: String){...}


...
} 7
Constructores
Cuando se define un constructor en una clase, puede contener:

●Ningún Parámetro
class A
● Parámetros
○ Sin marcar como var o val → estos solo existirán dentro del ámbito
del constructor
class B(x: Int)
○ Marcados como var o val → existirán en el ámbito de la instancia
class C(val y: Int) 8
Ejemplos de constructores
class A val aa = A()

val bb = B(12)
class B(x: Int)
println(bb.x)

=> compiler error unresolved reference


class C(val y: Int) val cc = C(42)

println(cc.y)
9
=> 42
Parámetros por defecto
Las instancias de la clase pueden tener valores por defecto.
●Usamos valores por defecto para reducir las necesidades del constructor
●Podemos mezclar parámetros por defecto con requeridos
●Más específicamente (no necesitamos tener múltiples versiones de un mismo constructor)

class Box(val length: Int, val width:Int = 20, val height:Int = 40)
val box1 = Box(100, 20, 40)
val box2 = Box(length = 100)
val box3 = Box(length = 100, width = 20, height = 40)
10
Constructor primario
Declaración del constructor primario fuera del encabezado de la clase.
class Circle(i: Int) {
init {
...
}
}
Esto es técnicamente igual a.
class Circle {
constructor(i: Int) {
...
}
11
}
Initializer block

● Todo código de inicialización es escrito en el bloque init


● Se permiten múltiples bloques init
● Los bloques init se convierten en el cuerpo del constructor

12
Ejemplo de initializer

Usar la palabra clave init:


class Square(val side: Int) {

init {
println(side * 2)

val s = Square(10)

=> 20 13
Múltiples constructores

● Usar la palabra clave constructor para definir constructores


secundarios
● Los constructores secundarios deben llamar:
○ Al constructor primario usando la palabra clave this

OR
○ A otro constructor secundario que llame al constructor primario

● El cuerpo de un constructor secundario no es requerido 14


Multiple constructors example

class Circle(val radius:Double) {

constructor(name:String) : this(1.0)

constructor(diameter:Int) : this(diameter / 2.0) {

println("in diameter constructor")

init {

println("Area: ${Math.PI * radius * radius}")

} 15
Propiedades

●Definimos las propiedades en la clase utilizando val o var

●Accede a esas propiedades utilizando la notación punto . con el nombre de la propiedad


●Setea esas propiedades utilizando la notación punto . con el nombre de la propiedad
(solo si fue declarada como var)

16
Clase persona con propiedad name

class Person(var name: String)

fun main() {

val person = Person("Alex")

println(person.name) Acceso con .<nombre proopiedad>

person.name = "Joey" Seteamos con .<nombre proopiedad>

println(person.name)

} 17
Custom getters y setters
Si no queremos tener el comportamiento por defecto de get/set:

● Sobreescribimos get() para una propiedad


● Sobreescribimos set() para una propiedad (si está definida como var)

Formato: var propertyName: DataType = initialValue


get() = ...
set(value) {
...
}
18
Custom getter
class Person(val firstName: String, val lastName:String) {
val fullName:String
get() {
return "$firstName $lastName"
} = Person("John", "Doe")
val person
}println(person.fullName)
=> John Doe
19
Custom setter
var fullName:String = ""
get() = "$firstName $lastName"
set(value) {
val components = value.split(" ")
firstName = components[0]
lastName = components[1]
field = value
}

person.fullName = "Jane Smith" 20


Member functions

● Las clases también puede tener funciones


● Las funciones se declaran como vimos en la clase 2 - Funciones
○ Usando la palabra clave fun
○ Pueden tener parámetros por defecto
○ Puede o no usarse un valor de retorno (Si no usamos devuelven Unit)

21
Herencia

22
Herencia

● Kotlin es un lenguaje de herencia simple


● Cada clase tiene sólo una clase padre, llamada superclase
● Cada subclase hereda todos los miembros de su superclase
incluyendo las que su superclase haya heredado

Si no queremos estar limitados a una única herencia, podemos definir una


interface ya que podemos implementar cuantas queramos.
23
Interfaces

● Proveen un contrato al cual las clases deben adherirse

● Pueden contener firmas de métodos y nombres de propiedades

● Pueden derivar de otras interfaces

Formato: interface NameOfInterface { interfaceBody }

24
Ejemplo de Interfaz
interface Shape {
fun computeArea() : Double
}
class Circle(val radius:Double) : Shape {
override fun computeArea() = Math.PI * radius * radius
}
val c = Circle(3.0)
println(c.computeArea())
=> 28.274333882308138 25
Extending classes

Para extender una clase:


● Creamos una nueva clase que utilice otra como su nucleo
(subclase)
● Agregamos funcionalidad a la clase sin crear una nueva
(funciones de extensión)

26
Creando una nueva clase

● Las clases en Kotlin por defecto no pueden ser herdadas

● Usamos la palabra clave open para permitirlo

● Las propiedades y funciones se sobreescriben con la palabra clave


override

27
Las clases son “final” por defecto

Declarar la clase

class A

Intentamos heredar A

class B : A

=>Error: A is final and cannot be inherited from

28
Uso de la palabra clave open
Usamos open para declarar que una clase puede ser “heredada”.

Declaramos la clase

open class C

Heredamos de C

class D : C()

29
Sobreescribiendo

● Debemos usar open para las propiedades y métodos que pueden ser
sobreescritos (de lo contrario tendremos un error de compilación)

● Debemos usar override cuando estemos sobreescribiendo métodos


y propiedades

● Algo marcado como override puede ser sobreescrito en las clases


hijas de esa clase (al menos que sea marcado como final)

30
Clases abstractas

● La clase debe marcarse como abstract


● No puede ser instanciada, debe ser heredada
● Es similar a las interfaces, pero tiene la habilidad de guardar un
estado
● Las propiedades y funciones marcadas como abstract debe
ser sobreescritas
● Pueden tener propiedades y métodos no abstractos 31
Ejemplo de una clase abstracta
abstract class Food {
abstract val kcal : Int
abstract val name : String
fun consume() = println("I'm eating ${name}")
}
class Pizza() : Food() {
override val kcal = 600
override val name = "Pizza"
}
fun main() {
Pizza().consume() // "I'm eating Pizza"
32
Cuando usar cada una
● Define un amplio espectro comportamiento para un tipo? Consideramos usar
una interfaz.

● El comportamiento es específico para el tipo? Consideramos una clase.

● Necesitamos heredar de múltiples clases? Consideramos refactorizar nuestro


código para ver si algún comportamiento puede ser aislado en una interfaz.

● Queremos dejar algunas propiedades / métodos abstractos para ser


redefinidos en una subclase? Consideramos una clase abstracta.

● Podemos heredar solo una clase, pero implementar muchas interfaces. 33


Extension functions (funciones de extensión

34
Extension functions
Agrega funciones a una clase que no podemos modificar de forma directa

● Parece que la implementación fuera de la clase

● No modificamos específicamente la clase original

● NO puede acceder a los miembros privados de la clase

Format: fun ClassName.functionName( params ) { body }

35
Por qué utilizar extension functions?

● Agrega funcionalidades a clases que no están abiertas

● Agrega funcionalidad a clases que no nos pertenecen

● Separa el núcleo de nuestra API de los métodos de ayuda de


clases que somos dueños

Definimos nuestras extension functions en un lugar que sea fácil de encontrar, por
ejemplo en el mismo archivo de la clase o en una función bien nombrada. 36
Ejemplo de extension function

Agrega isOdd() a la clase Int:

fun Int.isOdd(): Boolean { return this % 2 == 1 }

Llamamos isOdd() en un Int:

3.isOdd()

Las extension functions son muy poderosas en Kotlin! 37


Clases especiales

38
Clases data
● Clase especial que existe solo para almacenar datos

● Se marcan con la palabra clave data

● Genera los getters para cada propiedad (y setters para var


también)

● Genera los métodos toString(), equals(), hashCode(),


copy(), y operadores de desestructuración

Formato: data class <NameOfClass>( parameterList ) 39


Ejemplo de clase data

Definimos nuestra clase data:

data class Player(val name: String, val score: Int)

Usamos nuestra clase data:


val firstPlayer = Player("Lauren", 10)
println(firstPlayer)
=> Player(name=Lauren, score=10)

La clase data hace nuestro código más conciso! 40


Pair y Triple

● Pair y Triple son clases data predefinidas que guardan 2 o 3


piezas de datos respectivamente
● Accedemos a las variables con .first, .second, .third
respectivamente
● Normalmente clases data bien nombradas son una mejor opción
(nombres más acordes a los casos de uso)
41
Ejemplos de Pair y Triple
val bookAuthor = Pair("Harry Potter", "J.K. Rowling")

println(bookAuthor)

=> (Harry Potter, J.K. Rowling)

val bookAuthorYear = Triple("Harry Potter", "J.K. Rowling", 1997)


println(bookAuthorYear)

println(bookAuthorYear.third)

=> (Harry Potter, J.K. Rowling, 1997)


1997 42
Pair to
Pair's special to variante que permite omitir los paréntesis (infix function).

Código más legible


val bookAuth1 = "Harry Potter".to("J. K. Rowling")
val bookAuth2 = "Harry Potter" to "J. K. Rowling"
=> bookAuth1 and bookAuth2 are Pair (Harry Potter, J. K. Rowling)
También se pueden utilizar en colecciones como Maps y HashMaps

val map = mapOf(1 to "x", 2 to "y", 3 to "zz")


43
=> map of Int to String {1=x, 2=y, 3=zz}
Enum
Definición del usuario de un tipo de datos

● Usar this para pedir instancias de uno o varios valores


● El valor de la constante, por defecto, no es visible para nosotros
● Usar enum antes de la palabra clave class

Formato: enum class EnumName { NAME1, NAME2, … NAMEn }


Referencia por EnumName.<ConstantName>
44
Ejemplo de Enum
Definimos un enum con los colores red, green y blue.

enum class Color(val r: Int, val g: Int, val b: Int) {


RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255)
}

println("" + Color.RED.r + " " + Color.RED.g + " " + Color.RED.b)


=> 255 0 0
45
Object/singleton

● A veces queremos que exista solo una única instancia de una clase

● Usamos la palabra clave object en lugar de class

● La accedemos con NombreDelObjeto.<función o variable>

46
Ejemplo de Object/singleton
object Calculator {
fun add(n1: Int, n2: Int): Int {
return n1 + n2
}
}

println(Calculator.add(2,4))
=> 6
47
Companion objects

● Permite que todas las instancias de una clase compartan una


única instancia de un conjunto de variables o funciones.

● Usamos la palabra clave companion

● Referenciado por ClassName.PropertyOrFunction

48
Companion object example
class PhysicsSystem {
companion object WorldConstants {
val gravity = 9.8
val unit = "metric"
fun computeForce(mass: Double, accel: Double): Double {
return mass * accel
}
}
}
println(PhysicsSystem.WorldConstants.gravity)

println(PhysicsSystem.WorldConstants.computeForce(10.0, 10.0)) 49
Organizando nuestro código

50
Único archivo, múltiples entidades

● Kotlin NO obliga a la convención de una única (clase/interfaz)


por archivo

● Puedes y debes agrupar las clases relacionadas en un archivo

● Teniendo SIEMPRE en cuenta el largo del archivo o el desorden


que genera
51
Paquetes

● Proveen nombres para la organización

● Los identificadores en general son palabras en minúsculas, separadas por


un punto
● Declarado en la primera línea de código sin comentarios en un archivo
después de la palabra clave package

package org.example.game

52
Ejemplos de herencia
org.example.vehicle
Vehicle

org.example.vehicle.moped org.example.vehicle.car
Moped Car
Moped50cc Sedan
Moped100cc Hatchback 53
Modificadores de visibilidad

Usamos modificadores de visibilidad para limitar lo que exponemos.

● public significa que es visible fuera de la clase. Todo es público por


defecto, inclusive los métodos y variables de una clase.
● private significa que solo es visible dentro de esa clase (o en el archivo si
solo estamos trabajando con funciones).
● protected es igual que private, pero también será visible para las
subclases.
54
Como seguir

Podemos verificar lo aprendido


Lesson 3: Classes and objects

La lección original se encuentra en


Lesson 3: Classes and objects

55

También podría gustarte