Objetos complementarios
Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 13 de 57.
Algunos miembros pertenecen a una clase en su conjunto, en lugar de a una sola instancia. Kotlin los coloca en un companion object dentro de la clase, y los llamas mediante el nombre de la clase:
class Temperature(val celsius: Double) {
companion object {
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}Dentro de main:
val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)Salida:
100.0fromFahrenheit es una función factoría: una función que crea instancias. A diferencia de un constructor, tiene un nombre descriptivo y puede devolver null en lugar de un objeto. Con un private constructor, la factoría es la única vía de acceso, por lo que puede comprobar y limpiar primero la entrada:
class Username private constructor(val text: String) {
companion object {
fun of(raw: String): Username? {
val clean = raw.trim().lowercase()
return if (clean.length in 3..12) Username(clean) else null
}
}
}Dentro de main:
println(Username.of(" Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x") // error: el constructor es privadoSalida:
ada_l
nullEl objeto companion pertenece a la clase, por lo que puede llamar al constructor privado, y el propio código de la clase utiliza los miembros del companion sin un prefijo. Aquí cada ticket toma el número siguiente de un contador que comparten todos los tickets:
class Ticket(val holder: String) {
val number = nextNumber++
companion object {
private var nextNumber = 1
val issued: Int
get() = nextNumber - 1
}
}Dentro de main:
val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")Salida:
Ada #1, Bo #2
issued: 2Kotlin no tiene la palabra clave static. Los miembros del companion cumplen esa función: una sola copia para toda la clase, a la que se accede como ClassName.member. Como cualquier objeto, el companion se crea una vez, cuando la clase se utiliza por primera vez. Coloca en él aquello que no depende de una instancia, como las factorías y los contadores compartidos; todo lo que lea los datos de una instancia debe permanecer como un miembro normal.
class User private constructor(val name: String) {
fun greet() = "hi $name" // necesita una instancia: user.greet()
companion object {
fun guest() = User("guest") // pertenece a la clase: User.guest()
}
}Desafío
FácilCompleta la clase Coupon. Su constructor es privado, por lo que los cupones se crean mediante la fábrica Coupon.create(code, percent) en el objeto complementario. Devuelve null a menos que el código tenga entre 4 y 10 caracteres compuestos por letras mayúsculas y dígitos, y que el porcentaje esté entre 1 y 90. El objeto complementario también cuenta los cupones que ha creado en created, que el código externo puede leer, pero no modificar. apply(price) devuelve el precio menos el descuento, redondeado hacia abajo (price * (100 - percent) / 100).
El código proporcionado lee líneas SAVE10 10 200 (código, porcentaje, precio). Imprime SAVE10: 180 o invalid coupon SAVE10 y, finalmente, created: seguido del recuento.
Tu código va en Coupon.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
fun main() {
// Código de entrada/salida suministrado: déjalo como está
val input = generateSequence(::readLine).toList()
for (line in input) {
val (code, percent, price) = line.split(" ")
val coupon = Coupon.create(code, percent.toInt())
if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
}
println("created: ${Coupon.created}")
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesConstructores primariosBloques init y validaciónConstructores secundariosRepaso - Tarjeta de biblioteca4Herencia
Clases abiertas y subclasesSobrescritura de miembrossuper y constructoresClases abstractasRepaso: directorio del personal2Propiedades y encapsulación
Accesores personalizadosModificadores de visibilidadSetters privadoslazy y lateinitRepaso: cuenta bancaria5Interfaces
Declaración de interfacesMétodos predeterminadosInterfaces múltiplesInterfaces como tiposRepaso: métodos de pago8Genéricos
Funciones genéricasClases genéricasRestricciones de tiposVariancia con in y outRepaso - Cola de prioridad11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de libros3Objetos y complementos
Declaraciones de objetosObjetos complementariosConstantes con constExpresiones de objetosRepaso: taquillaPractica por tu cuenta: Playground de Kotlin