Está en la página 1de 259

Machine Translated by Google

Machine Translated by Google

7 clases  de  datos

Tratando  con Datos

Esa  función  copy()  
funcionó  perfectamente.  
Soy  como  tú  pero  más  alto.

Nadie  quiere  pasarse  la  vida  reinventando  la  rueda.
La  mayoría  de  las  aplicaciones  incluyen  clases  cuyo  objetivo  principal  es  almacenar  datos,  por  lo  que  

para  facilitar  su  vida  de  codificación,  los  desarrolladores  de  Kotlin  propusieron  el  concepto  de  una  

clase  de  datos.  Aquí,  aprenderá  cómo  las  clases  de  datos  le  permiten  escribir  código  que  es  más  

limpio  y  más  conciso  de  lo  que  nunca  soñó  que  fuera  posible.  Explorará  las  funciones  de  utilidad  de  la  

clase  de  datos  y  descubrirá  cómo  desestructurar  un  objeto  de  datos  en  sus  componentes.  En  el  

camino,  descubrirá  cómo  los  valores  de  parámetros  predeterminados  pueden  hacer  que  su  código  sea  

más  flexible  y  le  presentaremos  a  Any,  la  madre  de  todas  las  superclases.

este  es  un  nuevo  capitulo 191
Machine Translated by Google

detrás  de  escena  del  operador  ==

==  llama  a  una  función  llamada  igual
Como  ya  sabe,  puede  usar  el  operador  ==  para  verificar  la  igualdad.  
Detrás  de  escena,  cada  vez  que  usa  el  operador  ==,  llama  a  una  función  
llamada  igual.  Cada  objeto  tiene  una  función  de  igualdad,  y  la  
implementación  de  esta  función  determina  cómo  se  comportará  el  
operador  ==.

De  forma  predeterminada,  la  función  equals  comprueba  la  igualdad  
comprobando  si  dos  variables  contienen  referencias  al  mismo  objeto  subyacente.

Para  ver  cómo  funciona  esto,  supongamos  que  tenemos  dos  variables  
de  Wolf  llamadas  w1  y  w2.  Si  w1  y  w2  tienen  referencias  al  mismo  Wolf
objeto,  comparándolos  con  el  operador  ==  se  evaluará  como  verdadero:

val  w1  =  Lobo() w1  y  w2  se  refieren  al  

valor  w2  =  w1 mismo  objeto,  por  lo  que  
w1  ==  w2  es  verdadero.
//w1  ==  w2  es  verdadero

ÁRBITRO

w1 Lobo

ÁRBITRO
valle  lobo

w2

valle  lobo

Sin  embargo,  si  w1  y  w2  tienen  referencias  a  objetos  Wolf  separados,  
compararlos  con  el  operador  ==  se  evaluará  como  falso,  incluso  si  los  
objetos  tienen  valores  de  propiedad  idénticos.

val  w1  =  Lobo() w1  y  w2  se  refieren  a  
val  w2  =  Lobo() objetos  diferentes,  por  lo  
que  w1  ==  w2  es  falso.
//w1  ==  w2  es  falso

ÁRBITRO ÁRBITRO

Lobo Lobo
w1 w2

valle  lobo valle  lobo

Como  dijimos  anteriormente,  cada  objeto  que  crea  automáticamente  
incluye  una  función  de  igualdad.  Pero,  ¿de  dónde  viene  esta  
función?

192  Capítulo  7
Machine Translated by Google

clases  de  datos

equals  se  hereda  de  una  superclase  llamada  Any
Cada  objeto  tiene  una  función  llamada  igual  porque  su  clase  
hereda  la  función  de  una  clase  llamada  Cualquiera.  Class  Any  es  
la  madre  de  todas  las  clases:  la  superclase  definitiva  de  todo.  Cada  
Cada  clase  es  una  subclase  
clase  que  definas  es  una  subclase  de  Any  sin  que  tengas  que  
decirlo.  Entonces,  si  escribe  el  código  para  una  clase  llamada  
de  la  clase  Any  y  hereda  su  
myClass  que  se  ve  así:
comportamiento.  Cada  clase  
clase  MiClase  { ES­Un  tipo  de  Any  sin  que  
...

}
tengas  que  decirlo.

detrás  de  escena,  el  compilador  lo  convierte  automáticamente  en  esto:
Cualquier

clase  MiClase :  Cualquiera()  {
...
El  compilador  secretamente  convierte  
}
a  cada  clase  en  una  subclase  de  Any.

Mi  clase

La  importancia  de  ser  Any
Tener  Any  como  la  superclase  definitiva  tiene  dos  beneficios  clave:

¥ Asegura  que  cada  clase  herede  un  comportamiento  común.
La  clase  Any  define  un  comportamiento  importante  en  el  que  se  basa  el  sistema  
y,  como  cada  clase  es  una  subclase  de  Any,  este  comportamiento  lo  heredan  
todos  los  objetos  que  crea.  La  clase  Any  define  una  función  llamada  equals,  
por  ejemplo,  lo  que  significa  que  cada  objeto  hereda  automáticamente  esta  
función.

¥ Significa  que  puedes  usar  polimorfismo  con  cualquier  objeto.
Cada  clase  es  una  subclase  de  Any,  por  lo  que  cada  objeto  que  crea  tiene  
Any  como  su  último  supertipo.  Esto  significa  que  puede  crear  una  función  
con  cualquier  parámetro,  o  cualquier  tipo  de  devolución,  para  que  funcione  
con  todos  los  tipos  de  objetos.  También  significa  que  puede  crear  matrices  
polimórficas  para  contener  objetos  de  cualquier  tipo  utilizando  un  código  como  este:
El  compilador  detecta  que  
cada  objeto  de  la  matriz  
val  myArray  =  arrayOf(Coche(),  Guitarra(),  Jirafa())
tiene  un  supertipo  común  
de  Cualquiera,  por  lo  que  
Echemos  un  vistazo  más  de  cerca  al  comportamiento  común  heredado  de  
crea  una  matriz  de  tipo  Array<Any>.
la  clase  Any.

estas  aqui  4 193
Machine Translated by Google

Cualquier

El  comportamiento  común  definido  por  Any Cualquier

La  clase  Any  define  varias  funciones  que  son  heredadas  por  cada  clase.   igual()  
hashCode()  
Estos  son  los  que  más  nos  interesan,  junto  con  un  ejemplo  de  su  
toString()
comportamiento  predeterminado: ...

¥ equals(any:  Any):  Boolean  Te  dice  si  
dos  objetos  se  consideran  “iguales”.  De  forma  predeterminada,  devuelve  
verdadero  si  se  usa  para  probar  el  mismo  objeto  y  falso  si  se  usa  para   TuClaseAqui

probar  objetos  separados.  Detrás  de  escena,  la  función  de  igualdad  se  llama  
cada  vez  que  usa  el  operador  ==.

equals  devuelve   val  w1  =  Lobo() val  w1  =  Lobo()


falso  porque   valor  w2  =  w1
val  w2  =  Lobo()
w1  y  w2  contienen  
println(w1.equals(w2)) println(w1.equals(w2))
referencias  a  
diferentes  objetos. FALSO verdadero

equals  devuelve  verdadero  
porque  w1  y  w2  contienen  
¥ hashCode():  Int   referencias  al  mismo  objeto.  
Devuelve  un  valor  de  código  hash  para  el  objeto.  Ciertas  estructuras  de   Es  lo  mismo  que  probar  si  w1  ==  w2.
datos  los  utilizan  a  menudo  para  almacenar  y  recuperar  valores  de  
manera  más  eficiente.

val  w  =  Lobo()

println(w.hashCode())

Este  es  el  valor  
Por  defecto,  la  
523429237
del  código  hash  de  w.
función  de  igualdad
¥
comprueba  si  dos  
toString():  String  Devuelve  
un  mensaje  de  cadena  que  representa  el  objeto.  Por  defecto,  este  es  
el  nombre  de  la  clase  y  algún  otro  número  que  rara  vez  nos  importa. objetos  son  el  mismo  
objeto  subyacente.
val  w  =  Lobo()

println(w.toString())

lobo@1f32e575 La  función  igual
define  el  comportamiento  
La  clase  Any  proporciona  una  implementación  predeterminada  para  cada  una  
de  las  funciones  anteriores,  y  cada  clase  hereda  estas  implementaciones.  Sin   del  operador  ==.
embargo,  pueden  anularse  si  desea  cambiar  el  comportamiento  predeterminado  
de  cualquiera  de  estas  funciones.

194  Capítulo  7
Machine Translated by Google

clases  de  datos

Podríamos  querer  iguales  para  
comprobar  si  dos  objetos  son  equivalentes
Hay  algunas  situaciones  en  las  que  es  posible  que  desee  cambiar  la  
implementación  de  la  función  de  igualdad  para  cambiar  el  
comportamiento  del  operador  ==.

Suponga,  por  ejemplo,  que  tiene  una  clase  llamada  Receta  que  le  
permite  crear  objetos  que  contienen  datos  de  recetas.  En  esta  
situación,  puede  considerar  que  dos  objetos  Recipe  son  iguales  (o  
equivalentes)  si  contienen  detalles  de  la  misma  receta.  Entonces,  si  la  
clase  Receta  se  define  con  dos  propiedades  denominadas  título  y  es  
vegetariana  usando  un  código  como  este:
Receta
Receta  de  clase  (título  de  val:  cadena,  val  es  vegetariano:  booleano)  { el  

} titulo  es  vegetariano

es  posible  que  desee  que  el  operador  ==  se  evalúe  como  verdadero  
si  se  usa  para  comparar  dos  objetos  Receta  que  tienen  propiedades  
title  y  isVegetarian  coincidentes:

val  r1  =  Receta("Pollo  Bhuna",  false)
val  r2  =  Receta("Pollo  Bhuna",  falso)

ÁRBITRO título:  “Pollo  Bhuna”  es  
vegetariano:  falso
r1

Receta Estos  dos  objetos  tienen  valores  de  propiedad  
val  Receta
coincidentes,  por  lo  que  es  posible  que  
deseemos  que  el  operador  ==  se  evalúe  como  verdadero.

ÁRBITRO título:  “Pollo  Bhuna”  es  
vegetariano:  falso
r2

Receta
val  Receta

Si  bien  podría  cambiar  el  comportamiento  del  operador  ==  
escribiendo  código  adicional  para  anular  la  función  de  igualdad,  los  
desarrolladores  de  Kotlin  idearon  un  mejor  enfoque:  idearon  el  
concepto  de  una  clase  de  datos.  Averigüemos  cuál  es  uno  de  estos  y  
cómo  crear  uno.

estas  aqui  4 195
Machine Translated by Google

clases  de  datos

Una  clase  de  datos  le  permite  crear  objetos  de  datos
Una  clase  de  datos  es  aquella  que  le  permite  crear  objetos  cuyo  
objetivo  principal  es  almacenar  datos.  Incluye  características  que  son  
útiles  cuando  se  trata  de  datos,  como  una  nueva  implementación  de  la  
función  de  igualdad  que  comprueba  si  dos  objetos  de  datos  tienen  los  
mismos  valores  de  propiedad.  Esto  se  debe  a  que  si  dos  objetos  
almacenan  los  mismos  datos,  pueden  considerarse  iguales.

Una  clase  de  datos  se  define  anteponiendo  una  definición  de  clase  
normal  con  la  palabra  clave  de  datos .  El  siguiente  código,  por  ejemplo,  
cambia  la  clase  Receta  que  creamos  anteriormente  en  una  clase  de  datos:

El  prefijo  de  datos  
convierte  una  clase  
clase  de  datos  Receta  (título  de  valor:  cadena,  valor  es  vegetariano:  booleano)  {

normal  en  una  clase  de  datos. }
(Datos)
Receta
Cómo  crear  objetos  a  partir  de  una  clase  de  datos
el  
Crea  objetos  a  partir  de  una  clase  de  datos  de  la  misma  manera  que   titulo  es  vegetariano
crea  objetos  a  partir  de  una  clase  normal:  llamando  a  su  constructor.
El  siguiente  código,  por  ejemplo,  crea  un  nuevo  objeto  de  datos  de  
Receta  y  lo  asigna  a  una  nueva  variable  llamada  r1:

val  r1  =  Receta("Pollo  Bhuna",  false)

Las  clases  de  datos  anulan  automáticamente  su  función  de  igualdad  
para  cambiar  el  comportamiento  del  operador  ==  de  modo  que  
compruebe  la  igualdad  de  objetos  en  función  de  los  valores  de  las  
propiedades  de  cada  objeto.  Si,  por  ejemplo,  crea  dos  objetos  Receta  
que  contienen  valores  de  propiedad  idénticos,  comparar  los  dos  
objetos  con  el  operador  ==  se  evaluará  como  verdadero,  porque   título:  “Pollo  Bhuna”  es  
contienen  los  mismos  datos: vegetariano:  falso
ÁRBITRO

val  r1  =  Receta("Pollo  Bhuna",  false) Receta
r1
val  r2  =  Receta("Pollo  Bhuna",  falso)
//r1  ==  r2  es  cierto val  Receta
r1  y  r2  son título:  “Pollo  Bhuna”
es  vegetariano:  falso
Además  de  proporcionar  una  nueva   considerado  "igual"  ya  que  
ÁRBITRO
implementación  de  la  función  equals   los  dos  objetos  de  receta  
Receta
que  hereda  de  la  superclase  Any,  las   contienen  los  mismos  datos. r2
clases  de  datos  también  anulan  las  funciones  
hashCode  y  toString. val  Receta
Echemos  un  vistazo  a  cómo  se  
implementan.

196  Capítulo  7
Machine Translated by Google

clases  de  datos

Las  clases  de  datos  anulan  su  comportamiento  heredado

Una  clase  de  datos  necesita  que  sus  objetos  funcionen  bien  con  los  
datos,  por  lo  que  proporciona  automáticamente  las  siguientes  
implementaciones  para  las  funciones  equals,  hashCode  y  toString  
que  hereda  de  la  superclase  Any:
Los  objetos  de  
La  función  equals  compara  valores  de  propiedad datos  se  
Cuando  define  una  clase  de  datos,  su  función  de  igualdad  (y,  por  lo  tanto,  el  operador  
==)  continúa  devolviendo  verdadero  si  se  usa  para  probar  el  mismo  objeto. consideran  iguales  
Pero  también  devuelve  verdadero  si  los  objetos  tienen  valores  idénticos  para  las  
propiedades  definidas  en  su  constructor: si  sus  propiedades  
val  r1  =  Receta("Pollo  Bhuna",  false)
tienen  los  mismos  valores.
val  r2  =  Receta("Pollo  Bhuna",  falso)
println(r1.equals(r2))
verdadero

Los  objetos  iguales  devuelven  el  mismo  valor  hashCode Puede  pensar  en  un  código  hash  
como  una  etiqueta  en  un  balde.
Si  dos  objetos  de  datos  se  consideran  iguales  (en  otras  palabras,  tienen  valores  
Los  objetos  que  se  consideran  
de  propiedad  idénticos),  la  función  hashCode  devuelve  el  mismo  valor  para  cada   iguales  se  colocan  en  el  mismo  cubo  y  el  
objeto: código  hash  le  dice  al  sistema  dónde  

val  r1  =  Receta("Pollo  Bhuna",  false) buscarlos.
Los  objetos  iguales  DEBEN  tener  el  
val  r2  =  Receta("Pollo  Bhuna",  falso) mismo  valor  de  código  hash  ya  que  
println(r1.hashCode()) el  sistema  depende  de  esto.  
println(r2.hashCode()) Encontrará  más  información  sobre  

241131113 esto  en  el  Capítulo  9.

241131113

toString  devuelve  el  valor  de  cada  propiedad

Finalmente,  la  función  toString  ya  no  devuelve  el  nombre  de  la  clase  seguido  de  un  
número.  En  su  lugar,  devuelve  una  cadena  útil  que  contiene  el  valor  de  cada  propiedad  
definida  en  el  constructor  de  la  clase  de  datos:

val  r1  =  Receta("Pollo  Bhuna",  false)
println(r1.toString())
Receta(título=Pollo  Bhuna,  esVegetariano=falso)

Además  de  anular  las  funciones  que  hereda  de  la  superclase  Any,  una  
clase  de  datos  también  proporciona  características  adicionales  que  lo  
ayudan  a  manejar  los  datos  de  manera  más  efectiva,  como  la  capacidad  
de  copiar  un  objeto  de  datos.  Veamos  cómo  funciona  esto.

estas  aqui  4 197
Machine Translated by Google

función  de  copia

Copie  objetos  de  datos  utilizando  la  función  de  copia
Si  desea  crear  una  nueva  copia  de  un  objeto  de  datos,  alterando  algunas  
de  sus  propiedades  pero  dejando  el  resto  intacto,  puede  hacerlo  utilizando  la  
La  función  de  copia  le  
función  de  copia .  Para  usar,  llame  a  la  función  en  el  objeto  que  desea  copiar,  
pasando  los  nombres  de  las  propiedades  que  desea  modificar  junto  con  sus  
permite  copiar  un  objeto  de  
nuevos  valores.

Suponga  que  tiene  un  objeto  Receta  llamado  r1  que  se  define  mediante  
datos,  alterando  algunas  
un  código  como  este:
de  sus  propiedades.  El  
val  r1  =  Receta  ("Curry  tailandés",  falso) objeto  original  permanece  intacto.

ÁRBITRO
título:  “Curry  tailandés”  
es  vegetariano:  falso
r1

Receta
val  Receta

Si  quisiera  crear  una  copia  del  objeto  Receta,  alterando  el  valor  de  su  propiedad  
isVegetarian  a  verdadero,  podría  hacerlo  usando  la  función  de  copia  así:

val  r1  =  Receta  ("Curry  tailandés",  falso) Esto  copia  el  objeto  de  r1,  cambiando  el  valor  
val  r2  =  r1.copy(esVegetariano  =  verdadero) de  la  propiedad  isVegetarian  a  verdadero.

ÁRBITRO ÁRBITRO
título:  “Curry  tailandés”   título:  “Curry  tailandés”  
es  vegetariano:  falso es  vegetariano:  verdadero
r1 r2

Receta Receta
val  Receta val  Receta

Es  como  decir  "tome  una  copia  del  objeto  de  r1,  cambie  el  valor  de  su  propiedad  
isVegetarian  a  verdadero  y  asigne  el  nuevo  objeto  a  una  variable  llamada  r2".  
Crea  una  nueva  copia  del  objeto  y  deja  intacto  el  objeto  original.

Además  de  la  función  de  copia,  las  clases  de  datos  también  
proporcionan  un  conjunto  de  funciones  que  le  permiten  dividir  un  
objeto  de  datos  en  los  valores  de  propiedad  de  sus  componentes  en  
un  proceso  llamado  desestructuración.  Veamos  cómo.

198  Capítulo  7
Machine Translated by Google

clases  de  datos

Las  clases  de  datos  definen  funciones  de  componenteN...
Cuando  define  una  clase  de  datos,  el  compilador  agrega  automáticamente  
un  conjunto  de  funciones  a  la  clase  que  puede  usar  como  una  forma  
alternativa  de  acceder  a  los  valores  de  propiedad  de  su  objeto.  Estas  se  
conocen  como  funciones  de  componenteN,  donde  N  representa  el  número  de  
título:  “Pollo  Bhuna”  es  
la  propiedad  cuyo  valor  desea  recuperar  (en  orden  de  declaración).
vegetariano:  falso
Para  ver  cómo  funcionan  las  funciones  de  componenteN,  suponga  que  tiene  el   ÁRBITRO

siguiente  objeto  Receta: Receta
r
val  r  =  Receta("Pollo  Bhuna",  falso)
val  Receta
Si  desea  recuperar  el  valor  de  la  primera  propiedad  del  objeto  (su  propiedad  
de  título),  puede  hacerlo  llamando  a  la  función  componente1()  del  objeto  de  
“Pollo  Bhuna”
esta  manera:
componente1()  devuelve  
ÁRBITRO
la  referencia  contenida  por  
val  titulo  =  r.componente1()
la  primera  propiedad   Cadena
título
Esto  hace  lo  mismo  que  el  código: definida  en  el  constructor  
de  la  clase  de  datos.
val  título  =  r.título valor  cadena

pero  es  más  genérico.  Entonces,  ¿por  qué  es  tan  útil  que  una  clase  de  datos  
tenga  funciones  genéricas  de  ComponentN?

...que  te  permiten  desestructurar  objetos  de  datos
La  desestructuración  de  un  
Tener  funciones  de  componenteN  genéricas  es  útil  ya  que  proporciona  una  
forma  rápida  de  dividir  un  objeto  de  datos  en  sus  valores  de  propiedad  de  
objeto  de  datos  lo  divide  en  
componente,  o  de  desestructurarlo .

Suponga,  por  ejemplo,  que  desea  tomar  los  valores  de  propiedad  de  un  objeto   sus  partes  componentes.
Receta  y  asignar  cada  valor  de  propiedad  a  una  variable  separada.  En  lugar  de  
usar  el  código:

val  título  =  r.título

val  vegetariano  =  r.esvegetariano “Pollo  Bhuna”

para  procesar  explícitamente  cada  propiedad  a  su  vez,  puede  usar  el   ÁRBITRO

siguiente  código  en  su  lugar: Asigna  el  valor  de   Cadena


la  primera  propiedad   título
val  (título,  vegetariano)  =  r
de  r  al  título  y  el  valor   FALSO
El  código  anterior  es  como  decir  "cree  dos  variables,  title  y   de  la  segunda   valor  cadena
vegetarian,  y  asigne  uno  de  los  valores  de  propiedad  de  r  a  cada   propiedad  a   ÁRBITRO

una".  Hace  lo  mismo  que  el  código: vegetariano. booleano


vegetariano
val  titulo  =  r.componente1()
val  vegetariano  =  r.component2() valor  booleano

pero  es  más  conciso.

199
estas  aqui  4
Machine Translated by Google

==  contra  ===

Las  clases  de  datos  suenan  muy  bien,  pero  
me  preguntaba...  ¿Existe  una  forma  definitiva  de  
verificar  si  dos  variables  se  refieren  al  mismo  objeto  
subyacente?  Parece  que  no  puede  confiar  en  el  operador  ==  
porque  su  comportamiento  depende  de  cómo  se  haya  
implementado  la  función  de  igualdad,  y  esto  puede  variar  de  
una  clase  a  otra.

El  operador  ===  siempre  le  permite  verificar  si  dos  variables  se  
refieren  al  mismo  objeto  subyacente.

Si  desea  verificar  si  dos  variables  se  refieren  al  mismo  objeto  
subyacente,  independientemente  de  su  tipo,  debe  usar  el  operador  ===  
en  lugar  de  ==.  Esto  se  debe  a  que  el  operador  ===  siempre  se  evalúa  como  
verdadero  si  (y  solo  si)  las  dos  variables  contienen  una  referencia  al  mismo  
objeto  subyacente.  Esto  significa  que  si,  por  ejemplo,  tiene  dos  variables  
llamadas  x  e  y,  y  el  código:

x  ===  y

se  evalúa  como  verdadero,  entonces  sabe  que  las  variables  x  e  y  
deben  hacer  referencia  al  mismo  objeto  subyacente:

ÁRBITRO
==  comprueba  la  
X

equivalencia  de  objetos.
ÁRBITRO

===  comprueba  la   y

identidad  del  objeto.
A  diferencia  del  operador  ==,  el  operador  ===  no  depende  de  la  función  
de  igualdad  para  su  comportamiento.  El  operador  ===  siempre  se  
comporta  de  esta  manera  independientemente  del  tipo  de  clase.

Ahora  que  ha  visto  cómo  crear  y  usar  clases  de  datos,  creemos  un  
proyecto  para  el  código  de  Receta.

200  Capítulo  7
Machine Translated by Google

clases  de  datos

Crear  el  proyecto  Recetas
Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asígnele  el  nombre  
"Recetas".  Luego  cree  un  nuevo  archivo  de  Kotlin  llamado  Recipes.kt  resaltando  la  
carpeta  src ,  haciendo  clic  en  el  menú  Archivo  y  eligiendo  Nuevo  →  Archivo/clase  
de  Kotlin.  Cuando  se  le  solicite,  nombre  el  archivo  "Recetas"  y  elija  Archivo  en  la  
opción  Tipo.

Agregaremos  una  nueva  clase  de  datos  llamada  Receta  al  proyecto  y  crearemos  
Hemos  omitido  los  {}  porque  
algunos  objetos  de  datos  de  Receta.  Aquí  está  el  código:  actualice  su  versión  de  
Recipes.kt  para  que  coincida  con  la  nuestra: nuestra  clase  de  datos  no  tiene  cuerpo.

(Datos)
clase  de  datos  Receta  (título  de  valor:  cadena,  valor  es  vegetariano:  booleano) Receta

título
diversión  principal(argumentos:  Array<String>)  { es  vegetariano
val  r1  =  Receta  ("Curry  tailandés",  falso)
Cree  una  copia  de  r1,  
val  r2  =  Receta  ("Curry  tailandés",  falso)
modificando  su  propiedad  de  título.
val  r3  =  r1.copy(título  =  "Pollo  Bhuna")

println("código  hash  r1:  ${r1.hashCode()}")

println("código  hash  r2:  ${r2.hashCode()}")
Recetas
println("código  hash  r3:  ${r3.hashCode()}")

println("r1  a  la  Cadena:  ${r1.  a  la  Cadena()}")
origen
println("r1  ==  r2?  ${r1  ==  r2}")

println("r1  ===  r2?  ${r1  ===  r2}")
Recetas.kt
println("r1  ==  r3?  ${r1  ==  r3}") Desestructurar  r1.
val  (título,  vegetariano)  =  r1

println("título  es  $título  y  vegetariano  es  $vegetariano")

Prueba  de  conducción

Cuando  ejecuta  su  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  salida  del  IDE:

código  hash  r1:  ­135497891
código  hash  r2:  ­135497891
código  hash  r3:  241131113
r1  toString:  Receta  (título  =  curry  tailandés,  es  vegetariano  =  falso)  r1  
==  r2?  verdadero
r1  ==  r2  es  verdadero  porque  sus  objetos  tienen  valores  coincidentes.
r1  ===  r2?  FALSO
r1  ==  r3?  FALSO Como  se  refieren  a  objetos  separados,  r1  ===  r2  es  falso.

el  título  es  curry  tailandés  y  vegetariano  es  falso

estas  aqui  4 201
Machine Translated by Google

sin  preguntas  tontas

P:  Dijiste  que  cada  clase  es  una P:  ¿ Hay  alguna  regla  que  deba P:  He  notado  que  solo  has


subclase  de  Any.  Pensé  que  cada   ¿seguir? propiedades  de  clase  de  datos  definidas  
clase  solo  podía  tener  una  superclase   en  el  constructor  usando  val.  ¿ Puedo  
directa. R:  Lo  principal  es  que  si  anulas definirlos  usando  var  también?
la  función  equals ,  también  
R:  Detrás  de  escena,  Any debe  anular  la  función  hashCode . R:  Puede,  pero  le  recomendamos  enfáticamente
class  se  encuentra  en  la  raíz  de  cada  jerarquía   le  permite  hacer  que  sus  clases  de  datos  sean  
de  superclase,  por  lo  que  cada  clase  que  crea   Si  dos  objetos  se  consideran  iguales,   inmutables  creando  solo  propiedades  val .  Si  lo  
es  una  subclase  directa  o  indirecta  de deben  tener  el  mismo  valor  de  código  hash. hace,  significa  que  una  vez  que  se  ha  creado  un  
Algunas  colecciones  usan  códigos  hash  como
Cualquier.  Esto  significa  que  cada   objeto  de  datos,  no  se  puede  actualizar,  por  lo  que  
manera  eficiente  de  almacenar  objetos,  y   no  tiene  que  preocuparse  de  que  algún  otro  código  
clase  es  un  tipo  de  Any  y  hereda  las  
el  sistema  asume  que  si  dos  objetos  son   cambie  cualquiera  de  sus  propiedades.  Solo  tener  
funciones  que  define:  equals,  
iguales,  también  tienen  el  mismo  código  hash. propiedades  val  también  es  un  requisito  de  ciertas  
hashCode  y  toString.
Encontrará  más  información  sobre  esto  en  el  Capítulo  9. estructuras  de  datos.

P:  Ya  veo.  Y  dices  que  datos
P:  Eso  suena  complicado. P:  ¿ Por  qué  las  clases  de  datos  incluyen  un
¿Las  clases  anulan  automáticamente  
estas  funciones? función  de  copiar ?
R:  Sin  duda,  es  más  fácil  crear  una  base  de  datos
class,  y  el  uso  de  una  clase  de  datos  
R:  Sí.  Cuando  defines  un  dato R:  Las  clases  de  datos  generalmente  se  definen
significa  que  tendrá  un  código  más  
class,  el  compilador  anula  en  secreto   usando  propiedades  val  para  que  sean  
limpio  que  es  más  conciso.  Sin  
las  funciones  equals,  hashCode  y   inmutables.  Tener  una  función  de  copia  es  
embargo ,  si  desea  anular  las  
toString  que  la  clase  hereda  para  que   una  buena  alternativa  a  tener  objetos  de  datos  
funciones  equals,  hashCode  y  toString ,   que  se  pueden  modificar,  ya  que  le  permite  crear  
sean  más  apropiadas  para  objetos  cuyo  
puede  hacer  que  el  IDE  genere  la  mayor  
objetivo  principal  es  almacenar  datos. fácilmente  otra  versión  del  objeto  con  valores  de  
parte  del  código  por  usted. propiedad  modificados.

P:  ¿ Puedo  anular  estas  funciones?
Para  que  el  IDE  genere  implementaciones  
sin  crear  una  clase  de  datos? P:  ¿Puedo  declarar  que  una  clase  de  datos  es
para  las  funciones  equals,  hashCode  o  
¿abstracto?  O  abierto?
toString ,  comience  escribiendo  la  definición  
R:  Sí,  exactamente  de  la  misma  manera  que  usted de  clase  básica,  incluidas  las  propiedades.  
anular  funciones  de  cualquier  otra  clase:   Luego,  asegúrese  de  que  su  cursor  de  texto   R:  No.  Las  clases  de  datos  no  se  pueden  declarar.
al  proporcionar  una  implementación  para   abstracto  o  abierto,  por  lo  que  no  puede  usar  una  
esté  en  la  clase,  vaya  al  menú  Código  y  
las  funciones  en  el  cuerpo  de  su  clase. clase  de  datos  como  una  superclase.  Sin  embargo,  
seleccione  la  opción  Generar.  Finalmente,  
las  clases  de  datos  pueden  implementar  interfaces  
elija  la  función  para  la  que  desea  generar  
código. y,  desde  Kotlin  1.1,  también  pueden  heredar  de  
otras  clases.

202  Capítulo  7
Machine Translated by Google

clases  de  datos

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  bloque  
del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  candidato  
(a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  bloque.  Se  
utilizarán  todas  las  líneas  de  salida  y  algunas  líneas  de  salida  se  pueden  
utilizar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  bloques  de  código  
Mezclado candidatos  con  su  salida  correspondiente.

Mensajes
clase  de  datos  Película  (título  de  valor:  Cadena,  año  de  valor:  Cadena)

canción  de  clase  (título  de  valor:  cadena,  artista  de  valor:  cadena)

diversión  principal(argumentos:  Array<String>)  {
var  m1  =  Película  ("Pantera  Negra",  "2018")
var  m2  =  Película  ("Mundo  Jurásico",  "2015")
var  m3  =  Película  ("Mundo  Jurásico",  "2015")

var  s1  =  Canción  ("Love  Cats",  "The  Cure")
var  s2  =  Canción  ("Caballos  salvajes",  "Los  Rolling  Stones")
var  s3  =  Canción  ("Love  Cats",  "The  Cure")
El  código  de  
candidato  va  aquí.

Candidatos: Salida  posible:

imprimirln(m2  ==  m3)

imprimirln(s1  ==  s3)
Relaciona  
verdadero
cada   var  m4  =  m1.copia()  
candidato  
println(m1  ==  m4)
con  uno  de  
los  posibles  resultados.
var  m5  =  m1.copia()
imprimirln(m1  ===  m5)
FALSO

var  m6  =  m2
m2  =  m3

imprimirln(m3  ==  m6)

estas  aqui  4 203
Machine Translated by Google

solución  de  mensajes  mixtos

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  Se  utilizarán  todas  las  líneas  de  salida  y  algunas  líneas  de  salida  
se  pueden  utilizar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.

Mensajes
Solución
clase  de  datos  Película  (título  de  valor:  Cadena,  año  de  valor:  Cadena)

canción  de  clase  (título  de  valor:  cadena,  artista  de  valor:  cadena)

diversión  principal(argumentos:  Array<String>)  {
var  m1  =  Película  ("Pantera  Negra",  "2018")
var  m2  =  Película  ("Mundo  Jurásico",  "2015")
var  m3  =  Película  ("Mundo  Jurásico",  "2015")

var  s1  =  Canción  ("Love  Cats",  "The  Cure")
var  s2  =  Canción  ("Caballos  salvajes",  "Los  Rolling  Stones")
var  s3  =  Canción  ("Love  Cats",  "The  Cure")
El  código  de  
candidato  va  aquí.

m2  ==  m3  es  
Candidatos: Salida  posible:
verdadero  porque  
m1  y  m2  son  objetos  
imprimirln(m2  ==  m3)
de  datos.

imprimirln(s1  ==  s3)
m4  y  m1  tienen  
verdadero
valores  de  propiedad   var  m4  =  m1.copia()
coincidentes,  por  lo  
que  m1  ==  m4  es  
imprimirln(m1  ==  m4)
verdadero.
var  m5  =  m1.copia()  
m1  y  m5  son  
println(m1  ===  m5)
objetos  separados,  
FALSO
por  lo  que  m1  ===  
var  m6  =  m2
m5  es  falso.
m2  =  m3

imprimirln(m3  ==  m6)

204  Capítulo  7
Machine Translated by Google

clases  de  datos

Las  funciones  generadas  solo  
usan  propiedades  definidas  en  el  constructor
Hasta  ahora,  ha  visto  cómo  definir  una  clase  de  datos  y  agregar  propiedades  a  su  
constructor.  El  siguiente  código,  por  ejemplo,  define  una  clase  de  datos  denominada  
Receta  con  propiedades  denominadas  título  y  es  vegetariano: (Datos)
Receta
clase  de  datos  Receta  (título  de  valor:  cadena,  valor  es  vegetariano:  booleano)  {
el  
}
titulo  es  vegetariano

Al  igual  que  cualquier  otro  tipo  de  clase,  también  puede  agregar  propiedades  y  funciones  
a  una  clase  de  datos  incluyéndolas  en  el  cuerpo  de  la  clase.  Pero  hay  una  gran  captura.

Cuando  el  compilador  genera  implementaciones  para  funciones  de  clase  de  datos,  
como  anular  la  función  de  igualdad  y  crear  una  función  de  copia,  solo  incluye  las  
propiedades  definidas  en  el  constructor  principal.  Entonces,  si  agrega  propiedades  a  
una  clase  de  datos  definiéndolas  en  el  cuerpo  de  la  clase,  no  se  incluirán  en  ninguna  
de  las  funciones  generadas.
(Datos)
Suponga,  por  ejemplo,  que  agrega  una  nueva  propiedad  mainIngredient  al  cuerpo  de  
Receta
la  clase  de  datos  de  Receta  de  esta  manera:
título
clase  de  datos  Receta  (título  de  valor:  cadena,  valor  es  vegetariano:  booleano)  { esIngrediente  
"" principal  vegetariano
var  ingrediente  principal  =
}

Como  la  propiedad  mainIngredient  se  ha  definido  en  el  cuerpo  principal  
de  la  clase  en  lugar  del  constructor,  funciones  como  equals  la  ignoran.  Esto  
significa  que  si  crea  dos  objetos  Receta  usando  un  código  como  este: título:  “Curry  tailandés”  
es  vegetariano:  falso  
ingrediente  principal:  “pollo”
ÁRBITRO
val  r1  =  Receta  ("curry  tailandés",  falso)
Receta
r1.ingredienteprincipal  =  "Pollo" r1
val  r2  =  Receta  ("curry  tailandés",  falso)
val  Receta título:  “Curry  tailandés”  
r2.ingredienteprincipal  =  "Pato" es  vegetariano:  falso  
println(r1  ==  r2) //  se  evalúa  como  verdadero ingrediente  principal:  “pato”
ÁRBITRO

Receta
el  operador  ==  solo  mirará  el  título  y  las  propiedades   r2
r1  ==  r2  es  verdadero  porque  
isVegetarian  para  determinar  si  los  dos  objetos  son  iguales  porque  solo  
estas  propiedades  se  han  definido  en  el  constructor  de  la  clase  de  datos.   val  Receta r1  y  r2  tienen  un  título  
Si  los  dos  objetos  tienen  valores  diferentes  para  la  propiedad   coincidente  y  propiedades  vegetarianas.
mainIngredient  (como  en  el  ejemplo  anterior),  la  función  equals  no   El  operador  ==  ignora  la  
observará  esta  propiedad  al  considerar  si  dos  objetos  son  iguales. propiedad  mainIngredient  
porque  no  se  ha  definido  en  el  
constructor.
Pero,  ¿qué  sucede  si  su  clase  de  datos  tiene  muchas  propiedades  que  
desea  incluir  en  las  funciones  generadas  por  la  clase  de  datos?

estas  aqui  4 205
Machine Translated by Google

valores  de  parámetros  predeterminados

La  inicialización  de  muchas  propiedades  
puede  dar  lugar  a  un  código  engorroso

Como  acaba  de  aprender,  cualquier  propiedad  que  desee  incluir  en  las  funciones  
generadas  por  una  clase  de  datos  debe  definirse  en  su  constructor  principal.  Pero  si  
tiene  muchas  de  esas  propiedades,  su  código  puede  volverse  difícil  de  manejar  
rápidamente.  Cada  vez  que  crea  un  nuevo  objeto,  debe  especificar  un  valor  para  
cada  una  de  sus  propiedades,  por  lo  que  si  tiene  una  clase  de  datos  de  receta  que  
se  ve  así:
(Datos)

clase  de  datos  Receta  (título  de  valor:  Cadena, Receta

val  ingrediente  principal:  Cadena, título  
principalIngrediente  
val  es  vegetariano:  booleano,
esdificultad  
dificultad  val:  Cadena)  { vegetariana
}

su  código  para  crear  un  objeto  Receta  se  verá  así:

val  r  =  Receta  ("Curry  tailandés",  "Pollo",  falso,  "Fácil")

Esto  puede  no  parecer  tan  malo  si  su  clase  de  datos  tiene  una  pequeña  
cantidad  de  propiedades,  pero  imagine  que  necesita  especificar  los  valores  de  
10,  20  o  incluso  50  propiedades  cada  vez  que  necesita  crear  un  nuevo  objeto.  
Su  código  se  volvería  rápidamente  mucho  más  difícil  de  administrar. Cada  clase  de  datos  debe  
tener  un  constructor  principal,  
Entonces,  ¿qué  puedes  hacer  en  este  tipo  de  situación?
que  debe  definir  al  menos  un  
¡Valores  de  parámetros  predeterminados  al  rescate!
parámetro.
Si  su  constructor  define  muchas  propiedades,  puede  simplificar  las  llamadas  
asignando  un  valor  o  expresión  predeterminados  a  una  o  más  definiciones  de   Cada  parámetro  debe  
propiedad  en  el  constructor.  Así  es  como,  por  ejemplo,  asignaría  valores  
predeterminados  a  las  propiedades  isVegetarian  y  dificultad  en  el  constructor   tener  el  prefijo  val  o  var.
de  la  clase  Receta:

isVegetarian  tiene  un  
(Datos)
clase  de  datos  Receta  (título  de  valor:  Cadena,
valor  predeterminado  de  falso. Receta
val  ingrediente  principal:  Cadena,
título
val  es  vegetariano:  booleano  =  falso, el  ingrediente  
dificultad  val:  String  =  "Fácil")  { principal  es  la  
dificultad  vegetariana
}
la  dificultad  tiene  
un  valor  
Veamos  qué  diferencia  hace  esto  en  la  forma  en  que  creamos  nuevos  
predeterminado  de  "Fácil".
objetos  de  Receta.

206  Capítulo  7
Machine Translated by Google

clases  de  datos

Cómo  usar  los  valores  predeterminados  de  un  constructor

Cuando  tiene  un  constructor  que  usa  valores  predeterminados,  hay  
dos  formas  principales  de  llamarlo:  pasando  valores  en  orden  de  
declaración  y  usando  argumentos  con  nombre.  Veamos  cómo  funcionan  
ambos  enfoques.

1.  Pasar  valores  en  orden  de  declaración
Este  enfoque  es  el  mismo  que  ya  ha  estado  usando,  excepto  que  no  
necesita  proporcionar  valores  para  ningún  argumento  que  ya  tenga  
valores  predeterminados.

Supongamos,  por  ejemplo,  que  queremos  crear  un  objeto  Receta  
de  espaguetis  a  la  boloñesa  para  una  receta  que  no  es  vegetariana  y  es   No  hemos  especificado  valores  para  los  

fácil  de  hacer.  Podemos  crear  este  objeto  especificando  los  valores  de  las   valores  de  propiedad  isVegetarian  y  dificultad,  por  lo  

dos  primeras  propiedades  en  el  constructor  usando  el  siguiente  código: que  el  objeto  usa  sus  valores  predeterminados.

val  r  =  Receta("Espaguetis  a  la  boloñesa",  "Ternera") título:  “Espaguetis  a  la  boloñesa”  
ingrediente  principal:  “Carne  de  
El  código  anterior  asigna  valores  de  "Espaguetis  a  la  boloñesa"  
res”  es  vegetariano:  falsa  dificultad:  
y  "Carne  de  res"  
al  título  
principal.   Lyuego  
  las  puropiedades   del  
pingrediente  
sa  los  valores   redeterminados   ÁRBITRO
“Fácil”
especificados  en  el  constructor  para  las  propiedades  restantes. Receta
r

Puede  usar  este  enfoque  para  anular  los  valores  de  propiedad  
val  Receta
si  no  desea  usar  los  valores  predeterminados.  Si  quisiera  crear  
Asigna  a  isVegetarian  un  valor  de  
un  objeto  Receta  para  una  versión  vegetariana  de  espaguetis  a  
verdadero  y  usa  el  valor  predeterminado  
la  boloñesa,  por  ejemplo,  podría  usar  lo  siguiente:
para  la  propiedad  de  dificultad.

val  r  =  Receta("Espaguetis  a  la  boloñesa",  "Tofu",  verdadero)
título:  “Spaghetti  Bolognese”  
ingrediente  principal:  “Tofu”  
Esto  asigna  valores  de  "Espagueti  a  la  boloñesa",  "Tofu"  y  
isVegetarian:  verdadera  dificultad:  
verdadero  a  las  primeras  tres  propiedades  definidas  en  el   ÁRBITRO
“Fácil”
constructor  de  Recetas,  y  usa  el  valor  predeterminado  de  "Fácil"  
Receta
para  la  propiedad  de  dificultad  final. r

Tenga  en  cuenta  que  para  utilizar  este  enfoque,  debe  pasar  
val  Receta
los  valores  en  el  orden  en  que  se  declaran.  Por  ejemplo,  no  puede  
omitir  el  valor  de  la  propiedad  isVegetarian  si  desea  anular  el  
valor  de  la  propiedad  de  dificultad  que  viene  después.  El  siguiente  
código,  por  ejemplo,  no  es  válido:
Este  código  no  se  compilará,  
ya  que  el  compilador  espera  
val  r  =  Receta("Espaguetis  a  la  boloñesa",  "Ternera",  "Moderado") que  el  tercer  argumento  sea  
un  valor  booleano.
Ahora  que  ha  visto  cómo  funciona  pasar  valores  en  orden  de  declaración,  
veamos  cómo  usar  argumentos  con  nombre  en  su  lugar.

207
estas  aqui  4
Machine Translated by Google

argumentos  con  nombre

2.  Usar  argumentos  con  nombre
Debe  pasar  un  valor  
Llamar  a  un  constructor  usando  argumentos  con  nombre  le  permite  indicar  
explícitamente  qué  propiedad  debe  asignarse  qué  valor,  sin  tener  que  ceñirse  al   para  cada  argumento  
orden  en  que  se  definen  las  propiedades.
que  no  tenga  un  valor  
Supongamos,  por  ejemplo,  que  queremos  crear  un  objeto  Receta  de  
espaguetis  a  la  boloñesa  que  especifique  los  valores  de  las  propiedades  
predeterminado  asignado  
title  y  mainIngredient,  tal  como  lo  hicimos  anteriormente.  Para  hacer  esto  
usando  argumentos  con  nombre,  usaría  el  siguiente  código:
o  su  código  no  se  compilará.
val  r  =  Receta(título  =  "Espaguetis  a  la  boloñesa",
Esto  especifica  el  nombre  de  cada  
ingrediente  principal  =  "Carne  de  res") propiedad  y  el  valor  que  debe  tener.

El  código  anterior  asigna  valores  de  "Espaguetis  a  la  boloñesa"  y  
"Carne  de  res"  aLuego  
l  título  
uysa  
  las  
propiedades  
los   del  ingrediente  
valores  predeterminados   principal.  
especificados   título:  “Espaguetis  a  la  boloñesa”  
ingrediente  principal:  “Carne  de  
en  el  constructor  para  las  propiedades  restantes
res”  es  vegetariano:  falsa  dificultad:  
ÁRBITRO
“Fácil”
Tenga  en  cuenta  que  debido  a  que  estamos  usando  argumentos   Receta
r
con  nombre,  el  orden  en  que  especificamos  los  argumentos  no  importa.
El  siguiente  código,  por  ejemplo,  hace  lo  mismo  que  el  código  anterior  
val  Receta
y  es  igualmente  válido:
Con  argumentos  con  nombre,  el  
val  r  =  Receta  (ingrediente  principal  =  "Carne  de  res",
orden  en  que  especifica  el  valor  de  
title  =  "Espaguetis  a  la  boloñesa") cada  propiedad  no  importa.
La  gran  ventaja  de  usar  argumentos  con  nombre  es  que  solo  
necesita  incluir  argumentos  que  no  tengan  un  valor  
predeterminado,  o  cuyo  valor  predeterminado  desee  anular.  Si  
quisiera  anular  el  valor  de  la  propiedad  de  dificultad,  por  ejemplo,  
podría  hacerlo  usando  un  código  como  este:

val  r  =  Receta(título  =  "Espaguetis  a  la  boloñesa",
ingrediente  principal  =  "Carne  de  vacuno", título:  “Espaguetis  a  la  boloñesa”  
ingrediente  principal:  “Carne  de  
dificultad  =  "Moderado")
res”  es  vegetariano:  falso  dificultad:  
El  uso  de  valores  de  parámetros  predeterminados  y  argumentos  con  
ÁRBITRO
“Moderado”
Receta
nombre  no  solo  se  aplica  a  los  constructores  de  clases  de  datos;  también   r
puede  usarlos  con  constructores  o  funciones  de  clase  normales.  Le  

mostraremos  cómo  usar  valores  predeterminados  con  funciones  después  de   val  Receta
un  pequeño  desvío.

208  Capítulo  7
Machine Translated by Google

clases  de  datos

Constructores  secundarios
Al  igual  que  en  otros  lenguajes  como  Java,  las  clases  en  Kotlin  te  permiten  
definir  uno  o  más  constructores  secundarios.  Los  constructores  secundarios   Aunque  los  constructores  secundarios  
son  constructores  adicionales  que  le  permiten  pasar  diferentes  combinaciones   no  se  usan  mucho  en  Kotlinville,  
de  parámetros  para  crear  objetos.  Sin  embargo,  la  mayoría  de  las  veces  no  es   pensamos  en  brindarle  una  
necesario  utilizarlos,  ya  que  tener  valores  de  parámetros  predeterminados  es  
descripción  general  rápida  para  que  
muy  flexible. sepa  cómo  se  ven.
Aquí  hay  un  ejemplo  de  una  clase  llamada  Mushroom  que  define  dos  
constructores:  un  constructor  principal  definido  en  el  encabezado  de  la  
clase  y  un  constructor  secundario  definido  en  el  cuerpo  de  la  clase: Constructor  primario.

clase  Champiñón  (tamaño  de  val:  Int,  val  isMagic:  Boolean)  {

constructor(isMagic_param:  Boolean) :  this(0,  isMagic_param)  {
Constructor  
secundario. //Código  que  se  ejecuta  cuando  se  llama  al  constructor  secundario

Cada  constructor  secundario  comienza  con  la  palabra  clave  constructor  y  
va  seguido  del  conjunto  de  parámetros  que  se  usan  para  llamarlo.
Entonces,  en  el  ejemplo  anterior,  el  código:  

constructor(isMagic_param:  Boolean)

crea  un  constructor  secundario  con  un  parámetro  booleano.

Si  la  clase  tiene  un  constructor  primario,  cada  constructor  secundario  debe  delegar  
Esto  llama  al  constructor  principal  de  
en  él.  El  siguiente  constructor,  por  ejemplo,  llama  al  constructor  principal  de  la  
la  clase  actual.  Pasa  al  constructor  
clase  Mushroom  (usando  la  palabra  clave  this),  pasándole  un  valor  de  0  para  la  
principal  un  valor  de  0  para  el  tamaño  y  
propiedad  de  tamaño  y  el  valor  del  parámetro  isMagic_param  para  el  parámetro  
el  valor  de  isMagic_param  para  el  
isMagic:
parámetro  isMagic.

constructor(isMagic_param:  Boolean) :  this(0,  isMagic_param)

Puede  definir  código  adicional  que  el  constructor  secundario  debe  ejecutar  
cuando  se  llama  en  el  cuerpo  del  constructor  secundario:

constructor(isMagic_param:  Boolean) :  this(0,  isMagic_param)  {

//Código  que  se  ejecuta  cuando  se  llama  al  constructor  secundario

tamaño:  0
Finalmente,  una  vez  que  haya  definido  un  constructor  secundario,   ÁRBITRO
es  magia:  cierto
puede  usarlo  para  crear  objetos  usando  un  código  como  este:
metro
Champiñón
val  m  =  Hongo  (verdadero)

Champiñón

estas  aqui  4 209
Machine Translated by Google

valores  predeterminados

Las  funciones  también  pueden  usar  valores  predeterminados

Supongamos  que  tenemos  una  función  llamada  findRecipes  que  
busca  recetas  según  un  conjunto  de  criterios:

fun  findRecipes(título:  Cadena,
ingrediente:  Cuerda,
es  vegetariano:  booleano,
dificultad:  Cadena) :  Array<Receta>  {
//Código  para  encontrar  recetas
}

Cada  vez  que  llamamos  a  la  función,  debemos  pasarle  valores  para  los  
cuatro  parámetros  para  que  el  código  se  compile  así:

val  recetas  =  findRecipes("Curry  tailandés",  "",  false,  "")

Podemos  flexibilizar  la  función  asignando  a  cada  parámetro  un  valor  
predeterminado.  Si  lo  hace,  significa  que  ya  no  tenemos  que  pasar  los  
cuatro  valores  a  la  función  para  que  se  compile,  solo  los  que  queremos  
anular: Esta  es  la  misma  función  que  la  

fun  findRecipes(título:  String  =  "", anterior,  pero  esta  vez  le  hemos  dado  a  
cada  parámetro  un  valor  predeterminado.
ingrediente:  Cadena  =  "",
es  vegetariano:  booleano  =  falso,
dificultad:  Cadena  =  "") :  Array<Receta>  {
//Código  para  encontrar  recetas
}

Entonces,  si  quisiéramos  pasar  a  la  función  un  valor  de  "curry  tailandés"  
para  el  parámetro  del  título  y  aceptar  los  valores  predeterminados  para  el  
resto,  podríamos  usar  el  código:

val  recetas  =  findRecipes("Curry  tailandés") Ambos  llaman  a  la  función  findRecipes,  
usando  un  valor  de  "curry  tailandés"  
Y  si  quisiéramos  pasar  el  valor  del  parámetro  usando  argumentos  con  
para  el  argumento  del  título.
nombre,  podríamos  usar  lo  siguiente  en  su  lugar:

val  recetas  =  findRecipes(título  =  "curry  tailandés")

El  uso  de  valores  predeterminados  significa  que  puede  escribir  funciones  
que  son  mucho  más  flexibles.  Pero  hay  momentos  en  los  que  es  posible  que  
desee  escribir  una  nueva  versión  de  la  función  sobrecargándola .

210  Capítulo  7
Machine Translated by Google

clases  de  datos

Sobrecarga  de  una  función
La  sobrecarga  de  funciones  es  cuando  tiene  dos  o  más  funciones  con  el  mismo  
nombre  pero  con  diferentes  listas  de  argumentos.

Supongamos  que  tiene  una  función  llamada  addNumbers  que  se  parece  a  esto:

diversión  sumaNúmeros(a:  Int,  b:  Int) :  Int  {
devolver  a  +  b
Una  función  
}

La  función  tiene  dos  argumentos  Int,  por  lo  que  solo  puede  pasarle  valores   sobrecargada  es  
Int.  Si  quisiera  usarlo  para  sumar  dos  Dobles,  tendría  que  convertir  estos  valores  a  
solo  una  función  
Ints  antes  de  pasarlos  a  la  función.

diferente  que  tiene  
Sin  embargo,  puede  hacer  la  vida  mucho  más  fácil  para  la  persona  que  llama  
sobrecargando  la  función  con  una  versión  que  acepta  Doubles  en  su  lugar,  así: el  mismo  nombre  
diversión  sumaNúmeros(a:  Doble,  b:  Doble) :  Doble  { de  función  con  
devolver  a  +  b Esta  es  una  versión  sobrecargada  de  la  
} misma  función  que  usa  Doubles  en  lugar  de  Ints. diferentes  argumentos.
Esto  significa  que  si  llama  a  la  función  addNumbers  usando  el  código:
Una  función  
añadirNúmeros(2,  5) sobrecargada  
entonces  el  sistema  detectará  que  los  parámetros  2  y  5  son  Ints  y  llamará  a  la  versión   NO  es  lo  mismo  
Int  de  la  función.  Sin  embargo,  si  llama  a  la  función  addNumbers  usando:
que  una  función  anulada.
añadirNúmeros(1.6,  7.3)

entonces  el  sistema  llamará  a  la  versión  Doble  de  la  función  en  su  lugar,  ya  que  los  
parámetros  son  ambos  Dobles.

Qué  hacer  y  qué  no  hacer  con  la  sobrecarga  de  funciones:

¥ Los  tipos  de  devolución  pueden  ser  diferentes.
Puede  cambiar  el  tipo  de  devolución  de  una  función  sobrecargada,  siempre  que  las  listas  
de  argumentos  sean  diferentes.

¥ No  puede  cambiar  SOLO  el  tipo  de  devolución.
Si  solo  el  tipo  de  devolución  es  diferente,  no  es  una  sobrecarga  válida:  el  compilador  
asumirá  que  está  tratando  de  anular  la  función.  E  incluso  eso  no  será  legal  a  menos  
que  el  tipo  de  valor  devuelto  sea  un  subtipo  del  tipo  de  valor  devuelto  declarado  en  la  
superclase.  Para  sobrecargar  una  función,  DEBE  cambiar  la  lista  de  argumentos,  
aunque  puede  cambiar  el  tipo  de  retorno  a  cualquier  cosa.

estas  aqui  4 211
Machine Translated by Google

código  de  actualización

Actualicemos  el  proyecto  Recetas
Ahora  que  ha  aprendido  a  usar  valores  de  parámetros  predeterminados  y  funciones  
de  sobrecarga,  actualicemos  el  código  en  el  proyecto  Recetas.

Actualice  su  versión  del  código  en  el  archivo  Recipes.kt  para  que  coincida  con  
el  nuestro  a  continuación  (nuestros  cambios  están  en  negrita):

clase  de  datos  Receta  (título  de  valor:  Cadena,
(Datos)
Asigne  valores  
Agregue  nuevas  propiedades   val  ingrediente  principal:  Cadena, Receta
predeterminados  
de  ingrediente  principal  y  dificultad. val  es  vegetariano:  booleano  =  falso, título
a  las  propiedades  
dificultad  val:  String  =  "Fácil")  { isVegetarian  y   el  ingrediente  

dificultad. principal  es  la  
} Este  es  un  ejemplo  de  una  clase  con  un  constructor  
dificultad  vegetariana
secundario,  solo  para  que  puedas  ver  uno  en  acción.

clase  Champiñón  (tamaño  de  val:  Int,  val  isMagic:  Boolean)  {

constructor(isMagic_param:  Boolean) :  this(0,  isMagic_param)  {

//Código  que  se  ejecuta  cuando  se  llama  al  constructor  secundario
Champiñón
}
Este  es  un  ejemplo  de  una  función  que   el  
}
utiliza  valores  de  parámetros  predeterminados. tamaño  es  mágico

fun  findRecipes(título:  String  =  "",

ingrediente:  Cadena  =  "",

es  vegetariano:  booleano  =  falso,

dificultad:  Cadena  =  "") :  Array<Receta>  {

//Código  para  encontrar  recetas

return  arrayOf(Receta(título,  ingrediente,  es  vegetariano,  dificultad))

diversión  sumaNúmeros(a:  Int,  b:  Int) :  Int  { Recetas
devolver  a  +  b

} Estas  son  funciones  sobrecargadas. origen

diversión  sumaNúmeros(a:  Doble,  b:  Doble) :  Doble  { Recetas.kt
devolver  a  +  b

212  Capítulo  7
Machine Translated by Google

clases  de  datos

El  código  continuó... Hemos  cambiado  el  constructor  principal  de  la  receta,  por  lo  que  debemos  
cambiar  la  forma  en  que  se  llama  para  que  el  código  se  compile.
diversión  principal(argumentos:  Array<String>)  {
val  r1  =  Receta  ("Curry  tailandés",  "Pollo"  falso)
val  r2  =  Receta  (título  =  "Curry  tailandés",  ingrediente  principal  =  "Pollo"  falso)
val  r3  =  r1.copy(título  =  "Pollo  Bhuna")  println("Código  hash  r1:  
${r1.hashCode()}")  println("Código  hash  r2:  ${r2.hashCode()}")  
println( "código  hash  r3:  ${r3.hashCode()}")  println("r1  toString:   Recetas

${r1.toString()}")  println("r1  ==  r2?  ${r1  ==  r2}")  println( "r1  ===  
origen
r2?  ${r1  ===  r2}")  println("r1  ==  r3?  ${r1  ==  r3}")  val  (título,  
ingrediente  principal,  vegetariano,  dificultad)  =  r1  println  ( "  título  
es  $título  y  vegetariano  es  $vegetariano") Incluya  las  nuevas  propiedades  de  
Recetas.kt
Recipe  cuando  desestructuramos  r1.

Cree  un  hongo  llamando  a  su  constructor  principal.
val  m1  =  Hongo(6,  falso)
println("el  tamaño  de  m1  es  ${m1.size}  y  isMagic  es  ${m1.isMagic}")
val  m2  =  Hongo  (verdadero)
Crea  un  Mushroom  llamando  a  su  constructor  secundario.
println("el  tamaño  en  m2  es  ${m2.size}  y  isMagic  es  ${m2.isMagic}")

Llame  a  la  versión  Int  de  addNumbers.
println(agregarNúmeros(2,  5))
println(agregarNúmeros(1.6,  7.3))
Llame  a  la  versión  doble  de  addNumbers.
}

Prueba  de  conducción

Cuando  ejecuta  su  código,  el  siguiente  texto  se  imprime  en  la  ventana  
de  salida  del  IDE:
código  hash  r1:  295805076  
código  hash  r2:  295805076  
código  hash  r3:  1459025056  r1  
toString:  receta  (título  =  curry  tailandés,  ingrediente  principal  =  pollo,  es  vegetariano  =  falso,  dificultad  =  fácil)  r1  ==  r2?  
cierto  r1  ===  r2?  falso  r1  ==  r3?  el  título  falso  es  Thai  Curry  y  vegetariano  es  falso  m1  el  tamaño  es  6  e  isMagic  es  falso  
m2  el  tamaño  es  0  e  isMagic  es  verdadero  7  8.9

estas  aqui  4 213
Machine Translated by Google

sin  preguntas  tontas

P:  ¿Puede  una  clase  de  datos  incluir  funciones? P:  Quiero  que  los  programadores  de  Java  puedan  usar  mi  Kotlin
clases,  pero  Java  no  tiene  ningún  concepto  de  valores  de  parámetros  predeterminados.
¿Puedo  seguir  usando  valores  de  parámetros  predeterminados  en  mis  clases  de  Kotlin?
R:  Sí.  Usted  define  funciones  de  clase  de  datos  exactamente  de  la  misma  manera
que  defina  funciones  en  una  clase  que  no  sea  de  datos:  agregándolas  al  
cuerpo  de  la  clase. R:  Puedes.  Cuando  llamas  a  un  constructor  o  función  de  Kotlin  desde
Java,  solo  asegúrese  de  que  el  código  Java  especifique  un  valor  para  cada  
parámetro,  incluso  si  tiene  un  valor  de  parámetro  predeterminado.
P:  Los  valores  de  los  parámetros  predeterminados  parecen  muy  flexibles.

Si  planea  hacer  muchas  llamadas  Java  a  su  constructor  o  función  de  
R:  ¡Lo  son!  Puede  usarlos  en  constructores  de  clases  (incluidos
Kotlin,  un  enfoque  alternativo  es  anotar  cada  función  o  constructor  que  
constructores  de  clases  de  datos)  y  funciones,  e  incluso  puede  tener  un  
usa  valores  de  parámetros  predeterminados  con  @JvmOverloads.  Esto  
valor  de  parámetro  predeterminado  que  sea  una  expresión.  Esto  significa  que  
le  dice  al  compilador  que  cree  automáticamente  versiones  sobrecargadas  
puede  escribir  código  que  sea  flexible,  pero  muy  conciso.
que  se  pueden  llamar  más  fácilmente  desde  Java.

P:  Dijiste  que  usar  valores  de  parámetros  predeterminados  principalmente
soluciona  la  necesidad  de  escribir  constructores  secundarios.  
Aquí  hay  un  ejemplo  de  cómo  usa  @JvmOverloads  con  una  
¿Hay  alguna  situación  en  la  que  aún  pueda  necesitarlos? función:

R:  La  situación  más  común  es  si  necesita  extender  una  clase @JvmOverloads  fun  myFun(str:  String  =  ""){
en  un  marco  (como  Android)  que  tiene  múltiples  constructores. //El  código  de  la  función  va  aquí
}

Puede  obtener  más  información  sobre  el  uso  de  constructores  secundarios  en
Documentación  en  línea  de  Kotlin: Y  aquí  hay  un  ejemplo  de  cómo  lo  usa  con  una  clase  que  tiene  un  
constructor  principal:

https://kotlinlang.org/docs/reference/classes.html
clase  Foo  @JvmOverloads  constructor(i:  Int  =  0){
//Código  de  clase  se  encuentra  aquí
}

Tenga  en  cuenta  que  para  anotar  el  constructor  principal  
con  @JvmOverloads,  también  debe  prefijar  el  constructor  con  la  
palabra  clave  constructor .  La  mayoría  de  las  veces,  esta  palabra  
clave  es  opcional.

214  Capítulo  7
Machine Translated by Google

clases  de  datos

SER  el  compilador
Aquí  hay  dos  archivos  Kotlin  completos.
Su  trabajo  es  jugar  como  si  fuera  el  
compilador  y  determinar  si  cada  uno  
de  estos  archivos  se  
compilará.  Si  no  se  
compilan,  ¿cómo  los  
arreglaría?

estudiante  de  la  clase  de  datos  (val  firstName:  String,  val  lastName:  String,
val  casa:  Cadena,  val  año:  Int  =  1)

diversión  principal(argumentos:  Array<String>)  {
valor  s1  =  Estudiante("Ron",  "Weasley",  "Gryffindor")
val  s2  =  Estudiante("Draco",  "Malfoy",  casa  =  "Slytherin")
val  s3  =  s1.copy(firstName  =  "Fred",  año  =  3)
val  s4  =  s3.copy(firstName  =  "George")

valor  matriz  =  matrizDe(s1,  s2,  s3,  s4)
for  ((nombre,  apellido,  casa,  año)  en  matriz)  {
println("$firstName  $lastName  está  en  $house  year  $year")
}
}

estudiante  de  la  clase  de  datos  (val  firstName:  String,  val  lastName:  String,
val  casa:  Cadena,  val  año:  Int  =  1)

diversión  principal(argumentos:  Array<String>)  {
valor  s1  =  Estudiante("Ron",  "Weasley",  "Gryffindor")
val  s2  =  Estudiante(apellido  =  "Malfoy",  nombre  =  "Draco",  año  =  1)
val  s3  =  s1.copy(firstName  =  "Fred")
s3.año  =  3
val  s4  =  s3.copy(firstName  =  "George")

valor  matriz  =  matrizDe(s1,  s2,  s3,  s4)
for  (s  in  array)  { println("$
{s.firstName}  ${s.lastName}  está  en  ${s.house}  año  ${s.year}")
}
}

estas  aqui  4 215
Machine Translated by Google

ser  la  solución  del  compilador

SER  la  solución  del  compilador
Aquí  hay  dos  archivos  Kotlin  completos.
Su  trabajo  es  jugar  como  si  fuera  el  
compilador  y  determinar  si  cada  uno  
de  estos  archivos  se  
compilará.  Si  no  se  
compilan,  ¿cómo  los  
arreglaría?

estudiante  de  la  clase  de  datos  (val  firstName:  String,  val  lastName:  String,
val  casa:  Cadena,  val  año:  Int  =  1)
Esto  se  compilará  y  ejecutará  

con  éxito.  Imprime  los  valores  
diversión  principal(argumentos:  Array<String>)  {
de  propiedad  de  nombre,  
valor  s1  =  Estudiante("Ron",  "Weasley",  "Gryffindor")
val  s2  =  Estudiante("Draco",  "Malfoy",  casa  =  "Slytherin") apellido,  casa  y  año  para  cada  
val  s3  =  s1.copy(firstName  =  "Fred",  año  =  3) estudiante.
val  s4  =  s3.copy(firstName  =  "George")

Esta  línea  desestructura  
valor  matriz  =  matrizDe(s1,  s2,  s3,  s4)
cada  objeto  Student  en  la  matriz.
for  ((nombre,  apellido,  casa,  año)  en  matriz)  {
println("$firstName  $lastName  está  en  $house  year  $year")
}
}

estudiante  de  la  clase  de  datos  (val  firstName:  String,  val  lastName:  String,
val  casa:  Cadena,  val  año:  Int  =  1)

diversión  principal(argumentos:  Array<String>)  {
valor  s1  =  Estudiante("Ron",  "Weasley",  "Gryffindor")

val  s2  =  Estudiante(apellido  =  "Malfoy",  nombre  =  "Draco",  año  =  1,  casa  =  "Slytherin")  =  3)  val  s3  =  s1.copy(primerNombre  =  
"Fred",  año  s3.año  =  3

Esto  no  se  compilará  ya  que  se  requiere  un  valor  para  la  
val  s4  =  s3.copy(firstName  =  "George")
propiedad  de  la  casa  de  s2,  y  como  el  año  se  define  usando  

valor  matriz  =  matrizDe(s1,  s2,  s3,  s4) val,  su  valor  solo  se  puede  establecer  cuando  se  inicializa.
for  (s  in  array)  { println("$
{s.firstName}  ${s.lastName}  está  en  ${s.house}  año  ${s.year}")
}
}

216  Capítulo  7
Machine Translated by Google

clases  de  datos

Tu  caja  de  herramientas  de  Kotlin
Puede  descargar  
CAPÍTULO  
7 Tienes  el  Capítulo  7  en  tu  haber  y  ahora  tienes

clases  de  datos  añadidas  y  por  defecto
el  código  
completo  del  
capítulo  desde  https://
tinyurl.com/HFKotlin.
valores  de  parámetros  a  su  caja  de  herramientas.

El  comportamiento  del  operador  ==  está  determinado  por  la   Las  funciones  de  componenteN  le  permiten  desestructurar  
implementación  de  la  función  equals. objetos  de  datos  en  sus  valores  de  propiedad  de  
componente.
Cada  clase  hereda  una  función  equals,  hashCode  y  
toString  de  la  clase  Any  porque  cada  clase  es  una   Una  clase  de  datos  genera  sus  funciones  
subclase  de  Any.  Estas  funciones  se  pueden  anular. considerando  las  propiedades  definidas  en  su  constructor  
primario.

La  función  equals  te  dice  si  dos  objetos  se  consideran   Los  constructores  y  las  funciones  pueden  tener  

"iguales".  De  forma  predeterminada,  devuelve  verdadero  si   valores  de  parámetros  predeterminados.  Puede  llamar  a  
se  usa  para  probar  el  mismo  objeto  subyacente  y  falso  si  se   un  constructor  o  función  pasando  valores  de  parámetros  
usa  para  probar  objetos  separados. en  orden  de  declaración  o  usando  argumentos  con  nombre.

El  operador  ===  le  permite  verificar  si  dos  variables  se   Las  clases  pueden  tener  constructores  secundarios.
refieren  al  mismo  objeto  subyacente  independientemente  
Una  función  sobrecargada  es  una  función  diferente  
del  tipo  de  objeto.
que  tiene  el  mismo  nombre  de  función.
Una  clase  de  datos  le  permite  crear  objetos  cuyo   Una  función  sobrecargada  debe  tener  diferentes  

objetivo  principal  es  almacenar  datos.  Anula   argumentos,  pero  puede  tener  un  tipo  de  retorno  diferente.
automáticamente  las  funciones  equals,  hashCode  y  
toString,  e  incluye  funciones  de  copia  y  componenteN.

La  clase  de  datos  es  igual  a  la  función  verifica  la   Reglas  para  clases  
igualdad  al  observar  los  valores  de  propiedad  de  cada  
objeto.  Si  dos  objetos  de  datos  contienen  los  mismos  
un  
de  cdonstructor  
atos  *  Debe  
primario.
haber  
datos,  la  función  de  igualdad  devuelve  verdadero.
más  
*  El  p
constructor  
arámetros.principal  debe  definir  uno  o  
La  función  de  copia  le  permite  crear  una  nueva  copia  de  un  
objeto  de  datos,  modificando  algunas  de  sus  propiedades.  
El  objeto  original  permanece  intacto. *  Cada  parámetro  debe  estar  marcado  como
valor  o  var.

*Las  clases  de  datos  no  deben  estar  abiertas  o

abstracto.

estas  aqui  4 217
Machine Translated by Google
Machine Translated by Google

8 nulos  y  excepciones

Seguro  y Sonido

¡Ay,  Elvis!  Sé  que  mi  
código  está  a  salvo  contigo.

Todo  el  mundo  quiere  escribir  código  que  sea  seguro.
Y  la  buena  noticia  es  que  Kotlin  fue  diseñado  teniendo  en  cuenta  la  seguridad  del  código.  

Comenzaremos  mostrándole  cómo  el  uso  de  Kotlin  de  tipos  anulables  significa  que  casi  nunca  

experimentará  una  NullPointerException  durante  toda  su  estadía  en  Kotlinville.  Descubrirás  cómo  hacer  

llamadas  seguras  y  cómo  el  operador  Elvis  de  Kotlin  evita  que  te  emociones.  Y  cuando  hayamos  

terminado  con  nulos,  descubrirá  cómo  lanzar  y  capturar  excepciones  como  un  profesional.

este  es  un  nuevo  capitulo  219
Machine Translated by Google

eliminando  referencias

¿Cómo  se  eliminan  las  
referencias  a  objetos  de  las  variables?

Como  ya  sabe,  si  desea  definir  una  nueva  variable  Wolf  y  asignarle  una  
referencia  de  objeto  Wolf,  puede  hacerlo  usando  un  código  como  este:

var  w  =  Lobo()

El  compilador  detecta  que  desea  asignar  un  objeto  Wolf  a  la  variable  w,  
por  lo  que  infiere  que  la  variable  debe  tener  un  tipo  de  Wolf:

Como  está  asignando  un  objeto  Wolf  a  
una  variable,  el  compilador  infiere  que  el  
ÁRBITRO

tipo  de  variable  también  debe  ser  Wolf.

w Lobo

var  lobo

Una  vez  que  el  compilador  conoce  el  tipo  de  variable,  se  asegura  de  
que  solo  pueda  contener  referencias  a  objetos  Wolf,  incluidos  los  
subtipos  de  Wolf.  Entonces,  si  la  variable  se  define  usando  var,  puede  
actualizar  su  valor  para  que  contenga  una  referencia  a  un  objeto  Wolf  
completamente  diferente  usando,  por  ejemplo:

w  =  Lobo()
Eliminar  la  referencia  
a  este  objeto  Wolf...

ÁRBITRO

Lobo
w

var  lobo ...  y  asigne  una  referencia  
a  este  en  su  lugar.

Lobo

Pero,  ¿qué  sucede  si  desea  actualizar  la  variable  para  que  no  contenga  
ninguna  referencia  a  ningún  objeto?  ¿Cómo  elimina  una  referencia  de  
objeto  de  una  variable  una  vez  que  se  ha  asignado  una?

220  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Eliminar  una  referencia  de  objeto  usando  nulo
Si  desea  eliminar  una  referencia  a  un  objeto  de  una  variable,  puede  hacerlo  
asignándole  un  valor  nulo: El  significado  de  nulo
w  =  nulo
Cuando  establece  una  variable  
en  nulo,  es  como  desprogramar  un  
Un  valor  nulo  significa  que  la  variable  no  se  refiere  a  un  objeto:  la  variable  
control  remoto.  Tiene  un  control  
aún  existe,  pero  no  apunta  a  nada
remoto  (la  variable),  pero  ningún  
Pero  hay  una  gran  captura.  De  forma  predeterminada,  los  tipos  en  Kotlin  no   televisor  en  el  otro  extremo  (el  objeto).

aceptarán  valores  nulos.  Si  desea  que  una  variable  contenga  valores  nulos,  
debe  declarar  explícitamente  que  su  tipo  admite  valores  nulos. Una  referencia  nula  tiene  bits  que  representan  
"nulo",  pero  no  sabemos  ni  nos  importa  cuáles  
¿Por  qué  tener  tipos  anulables? son  esos  bits.  El  sistema  maneja  esto  
automáticamente  por  nosotros.
Un  tipo  anulable  es  aquel  que  permite  valores  nulos.  A  diferencia  de  
otros  lenguajes  de  programación,  Kotlin  rastrea  valores  que  pueden  ser  
nulos  para  evitar  que  realices  acciones  no  válidas  en  ellos.  Realizar  acciones  
no  válidas  en  valores  nulos  es  la  causa  más  común  de  problemas  de  tiempo  
de  ejecución  en  otros  lenguajes  como  Java,  y  puede  hacer  que  su  aplicación   Si  intenta  realizar  una  operación  no  válida  en  un  
se  bloquee  cuando  menos  lo  espera.  Sin  embargo,  estos  problemas  rara  vez   valor  nulo  en  Java,  se  enfrentará  a  una  
NullPointerException  grande  y  gorda.  Una  excepción  
ocurren  en  Kotlin  debido  a  su  uso  inteligente  de  tipos  anulables.
es  una  advertencia  que  le  dice  que  acaba  de  suceder  
algo  excepcionalmente  malo.  Veremos  las  excepciones  
Usted  declara  que  un  tipo  admite  valores  NULL  agregando  un  signo  de   con  más  detalle  más  adelante  en  este  capítulo.
interrogación  (?)  al  final  del  tipo.  Para  crear  una  variable  Wolf  anulable  y  
asignarle  un  nuevo  objeto  Wolf,  por  ejemplo,  usaría  el  código:

var  w:  ¿Lobo?  =  Lobo() w  es  un  Wolf?,  lo  que  significa  que  puede  
contener  referencias  a  objetos  Wolf  y  nulo.
ÁRBITRO

w
Un  tipo  anulable  es  
Lobo
var  Lobo?
aquel  que  puede  
Y  si  quisiera  eliminar  la  referencia  Wolf  de  la  variable,  usaría:
contener  valores  nulos  
Establecer  w  en  
w  =  nulo nulo  elimina  la  
referencia  al  objeto  Wolf. además  de  su  tipo  base.  
ÁRBITRO ¿Un  pato?  variable,  por  
w ejemplo,  aceptará  
var  Lobo? Lobo objetos  Duck  y  null.
Entonces,  ¿dónde  puedes  usar  tipos  anulables?

221
estas  aqui  4
Machine Translated by Google

dónde  usar  tipos  anulables

Puede  usar  un  tipo  que  acepta  valores  NULL  en  cualquier  lugar  

donde  pueda  usar  un  tipo  que  no  acepta  valores  NULL

Cada  tipo  que  defina  se  puede  convertir  en  una  versión  anulable  de  ese  tipo  simplemente  
agregando  un ?  hasta  el  final  de  la  misma.  Puede  usar  tipos  que  aceptan  valores  NULL  en  
los  mismos  lugares  en  los  que  usaría  tipos  antiguos  que  no  aceptan  valores  NULL:

¥ Al  definir  variables  y  propiedades.
Cualquier  variable  o  propiedad  puede  aceptar  valores  NULL,  pero  debe  definirla  
explícitamente  como  tal  declarando  su  tipo,  incluido  el ?.  El  compilador  no  puede  
inferir  cuándo  un  tipo  es  anulable  y,  de  forma  predeterminada,  siempre  creará  un  
tipo  no  anulable.  Entonces,  si  desea  crear  una  variable  de  cadena  anulable  llamada  
str  e  instanciarla  con  un  valor  de  "Pizza",  ¿debe  declarar  que  tiene  un  tipo  de  
cadena?  como  esto:

var  str:  ¿Cadena?  =  "Pizza"

Tenga  en  cuenta  que  las  variables  y  las  propiedades  se  pueden  instanciar  con  
nulo.  El  siguiente  código,  por  ejemplo,  compila  e  imprime  el  texto  "null":

var  str:  ¿Cadena?  =  nulo
Esto  es  diferente  a  decir  
“”
println(cadena) var  str:  String?  =  es  un  
“”
objeto  String  que  no  contiene  caracteres,  
¥ Al  definir  parámetros. mientras  que  null  no  es  un  objeto  String.
Puede  declarar  cualquier  tipo  de  parámetro  de  función  o  constructor  como  anulable.
El  siguiente  código,  por  ejemplo,  define  una  función  llamada  printInt  que  toma  un  
parámetro  de  tipo  Int?  (un  Int  anulable):

divertido  printInt(x:  Int?)  {
imprimir(x)
}

Cuando  define  una  función  (o  constructor)  con  un  parámetro  que  acepta  valores  NULL,  
aún  debe  proporcionar  un  valor  para  ese  parámetro  cuando  llama  a  la  función,  incluso  si  
ese  valor  es  NULL.  Al  igual  que  con  los  tipos  de  parámetros  que  no  aceptan  valores  NULL,  
no  puede  omitir  un  parámetro  a  menos  que  se  le  haya  asignado  un  valor  predeterminado.

¥ Al  definir  los  tipos  de  devolución  de  funciones.
Una  función  puede  tener  un  tipo  de  devolución  anulable.  La  siguiente  función,  por  
ejemplo,  tiene  un  tipo  de  devolución  Long?:
La  función  debe  devolver  un  valor  que  sea  Long  o  nulo.
resultado  divertido() :  ¿ Largo?  {
//¿Código  para  calcular  y  devolver  un  Long?
}

También  puede  crear  matrices  de  tipos  anulables.  Veamos  cómo.

222  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Cómo  crear  una  matriz  de  tipos  anulables
Una  matriz  de  tipos  anulables  es  aquella  cuyos  elementos  son  anulables.  
El  siguiente  código,  por  ejemplo,  crea  una  matriz  denominada  myArray  
que  contiene  String?s  (cadenas  que  aceptan  valores  NULL):
Un  Array<String?>  puede  
var  myArray:  Array<String?>  =  arrayOf("Hola",  "Hola") contener  cadenas  y  valores  nulos.

Sin  embargo,  el  compilador  puede  inferir  que  la  matriz  debe  contener  
tipos  anulables  si  la  matriz  se  inicializa  con  uno  o  más  elementos  nulos.  
Entonces,  cuando  el  compilador  ve  el  siguiente  código:

var  myArray  =  arrayOf("Hola",  "Hola",  nulo)

detecta  que  la  matriz  puede  contener  una  combinación  de  
cadenas  y  valores  nulos,  e  infiere  que  la  matriz  debe  tener  un  tipo  
de  Array<String?>:
"Hola" "Hola"

Cadena Cadena

ÁRBITRO nulo
ÁRBITRO ÁRBITRO ÁRBITRO

El  tercer  elemento  se  ha  inicializado  con  un  valor  
mi
Formación
nulo.  Como  la  matriz  contiene  cadenas  y  valores  

nulos,  crea  una  matriz  que  puede  contener  cadenas.

var  Matriz<Cadena?>
0 1 2

Ahora  que  ha  aprendido  a  definir  tipos  que  aceptan  valores  NULL,  veamos  
cómo  hacer  referencia  a  las  funciones  y  propiedades  de  su  objeto.

P:  ¿Qué  sucede  si  inicializo  una  variable  con  un  valor  nulo, P:  Usted  dijo  en  el  capítulo  anterior  que  cada  objeto  es  un
y  dejar  que  el  compilador  infiera  el  tipo  de  variable?  Por  ejemplo: subclase  de  Any.  ¿ Puede  una  variable  cuyo  tipo  es  Cualquiera  contener  
valores  nulos?
var  x  =  nulo

R:  No.  Si  desea  que  una  variable  contenga  referencias  a  cualquier  tipo  de
R:  El  compilador  ve  que  la  variable  debe  poder  contener objeto  y  valores  nulos,  su  tipo  debe  ser  Cualquiera?.  Por  ejemplo:
valores  nulos,  pero  como  no  tiene  información  sobre  ningún  otro  
tipo  de  objeto  que  pueda  necesitar,  crea  una  variable  que  solo  puede   var  z:  ¿Alguno?
contener  un  valor  nulo .  Probablemente  esto  no  sea  lo  que  desea,  
por  lo  que  si  va  a  inicializar  una  variable  con  un  valor  nulo,  asegúrese  
de  especificar  su  tipo.

estas  aqui  4 223
Machine Translated by Google

acceder  a  miembros  de  tipo  anulable

Cómo  acceder  a  las  funciones  y  propiedades  de  un  tipo  anulable
Suponga  que  tiene  una  variable  cuyo  tipo  admite  valores  NULL  y  desea  acceder  
a  las  propiedades  y  funciones  de  su  objeto.  No  puede  realizar  llamadas  a  
funciones  o  consultar  las  propiedades  de  un  valor  nulo,  ya  que  no  tiene  ninguna.  
Para  evitar  que  realice  operaciones  que  no  son  válidas,  el  compilador  insiste  en  
que  verifique  que  la  variable  no  sea  nula  antes  de  darle  acceso  a  cualquier  función  
o  propiedad.

¿Imagina  que  tienes  un  lobo?  variable  a  la  que  se  le  ha  asignado  una  
referencia  a  un  nuevo  objeto  Wolf  como  este:

var  w:  ¿Lobo?  =  Lobo()

Para  acceder  a  las  funciones  y  propiedades  del  objeto  subyacente,  primero  
debe  establecer  que  el  valor  de  la  variable  no  es  nulo.  Una  forma  de  lograr  
esto  es  verificar  el  valor  de  la  variable  dentro  de  un  if.  El  siguiente  código,  por  
ejemplo,  verifica  que  el  valor  de  w  no  sea  nulo  y  luego  llama  a  la  función  eat  del   comer()
objeto:
ÁRBITRO

si  (w !=  nulo)  {
w
comer() El  compilador  sabe  que  w  no  es  nulo,  por  
} lo  que  puede  llamar  a  la  función  eat(). var  Lobo? Lobo

Puede  utilizar  este  enfoque  para  crear  condiciones  más  complejas.  El  
siguiente  código,  por  ejemplo,  verifica  que  el  valor  de  la  variable  w  no  sea  nulo  
y  luego  llama  a  su  función  comer  cuando  su  propiedad  hambre  es  menor  que  5:

if  (w !=  null  &&  w.hambre  <  5)  {
comer()
El  lado  derecho  de  &&  solo  se  ejecuta  si  el  lado  izquierdo  es  verdadero,  por  lo  que  
}
aquí,  el  compilador  sabe  que  w  no  puede  ser  nulo  y  le  permite  llamar  a  w.hunger.
Sin  embargo,  hay  algunas  situaciones  en  las  que  este  tipo  de  código  aún  puede  fallar.
Si  la  variable  w  se  usa  para  definir  una  propiedad  var  en  una  clase,  por  ejemplo,  es  
posible  que  se  le  haya  asignado  un  valor  nulo  entre  la  comprobación  de  nulo  y  su  
uso,  por  lo  que  el  siguiente  código  no  se  compilará:

clase  Mi  Lobo  {
var  w:  ¿Lobo?  =  Lobo()

diversión  myFunction()  {
si  (w !=  nulo){
comer()
} Esto  no  se  compilará  porque  el  compilador  no  puede  
} garantizar  que  algún  otro  código  no  actualice  la  propiedad  w  
entre  la  comprobación  de  que  no  es  nulo  y  su  uso.
}

Afortunadamente,  existe  un  enfoque  más  seguro  que  evita  este  tipo  de  problema.

224  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Mantén  las  cosas  seguras  con  llamadas  seguras

Si  desea  acceder  a  las  propiedades  y  funciones  de  un  tipo  que  acepta  valores   ?.  es  el  operador  de  
NULL,  un  enfoque  alternativo  es  usar  una  llamada  segura.  Una  llamada  segura  le  
permite  acceder  a  funciones  y  propiedades  en  una  sola  operación  sin  tener  que   llamadas  seguras.  Le  
realizar  una  verificación  nula  por  separado.

Para  ver  cómo  funcionan  las  llamadas  seguras,  imagina  que  tienes  un  Wolf.  
permite  acceder  de  
propiedad  (como  antes)  que  contiene  una  referencia  a  un  objeto  Wolf  así:
forma  segura  a  las  
var  w:  ¿Lobo?  =  Lobo()
funciones  y  propiedades  
Para  hacer  una  llamada  segura  a  la  función  de  comer  de  Wolf,  usaría  el  siguiente  
código: de  un  tipo  anulable.
w?.comer() El ?.  significa  que  solo  se  llama  a  eat()  si  w  no  es  nulo.

Esto  solo  llamará  a  la  función  comer  de  Wolf  cuando  w  no  sea  nulo.  Es  como  decir  "si  w  
no  es  nulo,  llame  a  comer".

De  manera  similar,  el  siguiente  código  hace  una  llamada  segura  a  la  propiedad  hambre  de  w:

w?.hambre

Si  w  no  es  nulo,  la  expresión  devuelve  una  referencia  al  valor  de  la  propiedad  
hambre.  Sin  embargo,  si  w  es  nulo,  el  valor  de  la  expresión  completa  se  evalúa  
como  nulo.  Estos  son  los  dos  escenarios:

A Escenario  A:  w  no  es  nulo.
La  variable  w  contiene  una  referencia  a  un  objeto  Wolf  y  el  valor  de  su  propiedad  
hambre  es  10.  El  código  w?.hunger  se  evalúa  como  10.

w?.hambre
//Devuelve  10 ÁRBITRO

hambre:  10
w

var  Lobo? Lobo

B Escenario  B:  w  es  nulo.
La  variable  w  contiene  un  valor  nulo,  no  Wolf,  por  lo  que  la  expresión  completa  se  
evalúa  como  nula.

w?.hambre
Soy  nulo,  por  lo  que  no  tengo  
//Devuelve  nulo
ÁRBITRO acceso  a  las  propiedades  de  Wolf.

var  Lobo?

estas  aqui  4 225
Machine Translated by Google

la  cadena

Puedes  encadenar  llamadas  seguras  juntas

Otra  ventaja  de  usar  llamadas  seguras  es  que  puede  encadenarlas  
para  formar  expresiones  poderosas  pero  concisas.

Suponga  que  tiene  una  clase  llamada  MyWolf  que  tiene  un  solo  
lobo.  propiedad  llamada  w.  Aquí  está  la  definición  de  clase:

clase  Mi  Lobo  {
var  w:  ¿Lobo?  =  Lobo()

Supongamos  también  que  tienes  un  MyWolf?  variable  llamada  myWolf  
así:

var  mi  Lobo:  Mi  Lobo?  =  Mi  Lobo()

Si  desea  obtener  el  valor  de  la  propiedad  hambre  para  Wolf  de  la  
variable  myWolf,  puede  hacerlo  usando  un  código  como  este:

mi  lobo?.w?.hambre Si  myWolf  no  es  nulo  y  w  no  es  nulo,  obtenga  hambre.  De  lo  contrario,  utilice  nulo.

Es  como  decir  “Si  myWolf  o  w  es  nulo,  devuelve  un  valor  nulo.
De  lo  contrario,  devolver  el  valor  de  la  propiedad  hambre  de  
w  ”.  La  expresión  devuelve  el  valor  de  la  propiedad  hambre  si  (y  solo  si)  
myWolf  y  w  no  son  nulos.  Si  myWolf  o  w  son  nulos,  la  expresión  completa  
se  evalúa  como  nula.

Qué  sucede  cuando  se  evalúa  una  cadena  de  llamadas  segura

Analicemos  lo  que  sucede  cuando  el  sistema  evalúa  la  cadena  de  
llamadas  seguras:

mi  lobo?.w?.hambre

1 El  sistema  primero  verifica  que  myWolf  no  sea  nulo.
Si  myWolf  es  nulo,  la  expresión  completa  se  evalúa  como  nula.  Si  myWolf  no  es  
nulo  (como  en  este  ejemplo),  el  sistema  continúa  con  la  siguiente  parte  de  la  
expresión.

mi  lobo?.w?.hambre
ÁRBITRO

Mi  lobo

var  Mi  Lobo? Mi  lobo

226  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

La  historia  continúa...

2 Luego,  el  sistema  verifica  que  la  propiedad  w  de  myWolf  no  sea  nula.
Siempre  que  myWolf  no  sea  nulo,  el  sistema  pasa  a  la  siguiente  parte  de  la  
expresión,  la  w?  parte.

Si  w  es  nulo,  la  expresión  completa  se  evalúa  como  nula.  Si  w  no  es  nulo,  como  
en  este  ejemplo,  el  sistema  pasa  a  la  siguiente  parte  de  la  expresión.

mi  lobo?.w?.hambre
ÁRBITRO

ÁRBITRO
w

var  Lobo? Lobo
Mi  lobo

var  Mi  Lobo? Mi  lobo

3 Si  w  no  es  nulo,  devuelve  el  valor  de  la  propiedad  de  hambre  de  w.
Siempre  que  ni  la  variable  myWolf  ni  su  propiedad  w  sean  nulas,  la  expresión  
devuelve  el  valor  de  la  propiedad  hambre  de  w.  En  este  ejemplo,  la  expresión  
se  evalúa  como  10.

mi  lobo?.w?.hambre
ÁRBITRO

hambre:  10
ÁRBITRO
w

var  Lobo? Lobo
Mi  lobo

var  Mi  Lobo? Mi  lobo

Entonces,  como  puede  ver,  las  llamadas  seguras  se  pueden  encadenar  
para  formar  expresiones  concisas  que  son  muy  poderosas  pero  seguras.
Pero  ese  no  es  el  final  de  la  historia.

estas  aqui  4 227
Machine Translated by Google

llamadas  seguras

Puedes  usar  llamadas  seguras  para  asignar  valores...

Como  era  de  esperar,  puede  usar  llamadas  seguras  para  asignar  un  valor  a  una  
variable  o  propiedad.  Si  tienes  un  lobo?  variable  llamada  w,  por  ejemplo,  puede  
asignar  el  valor  de  su  propiedad  hambre  a  una  nueva  variable  llamada  x  usando  un  
Si  la  propiedad  hambre  de  w  es  10,  
código  como  este:
var  x  =  w?.hunger  crea  un  Int?  
variable  con  un  valor  de  10.
var  x  =  w?.hambre

Es  como  decir  "Si  w  es  nulo,  establezca  x  en  nulo,  de  lo  contrario,  establezca  x  
en  el  valor  de  la  propiedad  de  hambre  de  w  ".  Como  la  expresión: ÁRBITRO

10
w?.hambre X

puede  evaluar  a  un  valor  Int  o  nulo,  el  compilador  infiere  que  x  debe  tener  un  tipo  de   var  Int? En  t

Int?.

...y  asigna  valores  a  las  llamadas  seguras
También  puede  usar  una  llamada  segura  en  el  lado  izquierdo  de  una  variable  

o  asignación  de  propiedad.

Suponga,  por  ejemplo,  que  desea  asignar  un  valor  de  6  a  la  propiedad  de  hambre  
de  w,  siempre  que  w  no  sea  nulo.  Puedes  lograr  esto  usando  el  código:
Si  w  no  es  nulo,  
w?.hunger  =  6  
w?.hambre  =  6 establece  la  propiedad  de  hambre  de  w  en  6.

El  código  verifica  el  valor  de  w,  y  si  no  es  nulo,  el  código  asigna  un  valor  de  6  a  la   ÁRBITRO

propiedad  hambre.  Sin  embargo,  si  w  es  nulo,  el  código  no  hace  nada. hambre:  10  6
w

También  puede  usar  cadenas  de  llamadas  seguras  en  esta  situación.  El  siguiente   var  Lobo? Lobo


código,  por  ejemplo,  solo  asigna  un  valor  a  la  propiedad  hambre  si  myWolf  y  w  no  son  
nulos:

mi  lobo?.w?.hambre  =  2

Es  como  decir  "si  myWolf  no  es  nulo,  y  el  valor  de  la  propiedad  w  de  myWolf  no  
es  nulo,  entonces  asigne  un  valor  de  2  a  la  propiedad  de  hambre  de  w  ":

ÁRBITRO

hambre:  2
ÁRBITRO
w

Lobo el  hambre  se  establece  en  2  solo  
var  Lobo?
Mi  lobo si  myWolf  y  w  no  son  nulos.

var  Mi  Lobo? Mi  lobo

Ahora  que  sabe  cómo  hacer  llamadas  seguras  a  tipos  que  aceptan  valores  NULL,  
pruebe  el  siguiente  ejercicio.

228  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

SER  el  compilador
Cada  uno  de  los  archivos  de  Kotlin  en  
esta  página  representa  un  archivo  fuente   Esta  es  la  salida  requerida.

completo.  Su  trabajo  es  jugar  como  si  fuera   Misty:  ¡Miau!
el  compilador  y  determinar  si   Calcetines:  ¡Miau!
cada  uno  de  estos  archivos  
compilará  y  producirá  el  
resultado  de  la  derecha.  ¿Si  no,  porque  no?

A clase  Gato(var  nombre:  String?  =  "")  { divertido  Miau()   B class  Cat(var  nombre:  String?  =  null)  {
{ println("¡Miau!") } divertido  Miau()  { println("¡Miau!") }
} }

diversión  principal(argumentos:  Array<String>)  { diversión  principal(argumentos:  Array<String>)  {
var  myCats  =  arrayOf(Cat("Misty"), var  myCats  =  arrayOf(Cat("Misty"),
nulo, Gato  (nulo),
Gato("Calcetines"))   Gato("Calcetines"))  
para  (gato  en  myCats)  { para  (gato  en  myCats)  {
if  (gato!=  nulo)  { print("$ print("${gato.nombre}:  ")
{gato.nombre}:  ")  gato.Miau() gato.Miau()
}
} }
}
}

C class  Cat(var  nombre:  String?  =  null)  { D clase  Gato(var  nombre:  String  =  "")  {
divertido  Miau()  { println("¡Miau!") } divertido  Miau()  { println("¡Miau!") }
} }

diversión  principal(argumentos:  Array<String>)  { diversión  principal(argumentos:  Array<String>)  {
var  myCats  =  arrayOf(Cat("Misty"), var  myCats  =  arrayOf(Cat("Misty"),
nulo, Gato  (nulo),
Gato  ("Calcetines")) Gato  ("Calcetines"))
for  (gato  en  myCats)  { print("$ para  (gato  en  myCats)  {
{gato?.nombre}:  ") if  (gato!=  nulo)  { print("$
¿gato?.  Miau() {gato?.nombre}:  ")  gato?.Miau()
}
} }
}
}

estas  aqui  4 229
Machine Translated by Google

ser  la  solución  del  compilador

SER  la  solución  del  compilador
Cada  uno  de  los  archivos  de  Kotlin  en  
esta  página  representa  un  archivo  fuente   Esta  es  la  salida  requerida.

completo.  Su  trabajo  es  jugar  como  si  fuera   Misty:  ¡Miau!
el  compilador  y  determinar  si   Calcetines:  ¡Miau!
cada  uno  de  estos  archivos  
compilará  y  producirá  el  
resultado  de  la  derecha.  ¿Si  no,  porque  no?

A clase  Gato(var  nombre:  String?  =  "")  { divertido  Miau()   B class  Gato(var  nombre:  String?  =  null)  { divertido  Miau()  
{ println("¡Miau!") } { println("¡Miau!") }
} }

diversión  principal(argumentos:  Array<String>)  { diversión  principal(argumentos:  Array<String>)  {
var  myCats  =  arrayOf(Cat("Misty"), var  myCats  =  arrayOf(Cat("Misty"),
nulo, Gato  (nulo),
Gato("Calcetines"))   Gato("Calcetines"))  
para  (gato  en  myCats)  { para  (gato  en  myCats)  {
if  (gato!=  nulo)  { print("$ print("${gato.nombre}:  ")
{gato.nombre}:  ")  gato.Miau() gato.Miau()
}
Esto  compila,  pero  el  resultado  es  
} }
Esto  compila  y  produce  la   incorrecto  (el  segundo  Gato  con  un  
}
} salida  correcta. nombre  nulo  también  Meows).

C class  Cat(var  nombre:  String?  =  null)  { D clase  Gato(var  nombre:  String  =  "")  {
divertido  Miau()  { println("¡Miau!") } divertido  Miau()  { println("¡Miau!") }
} }

diversión  principal(argumentos:  Array<String>)  { diversión  principal(argumentos:  Array<String>)  {
var  myCats  =  arrayOf(Cat("Misty"), var  myCats  =  arrayOf(Cat("Misty"),
nulo, Gato  (nulo),
Gato  ("Calcetines")) Gato  ("Calcetines"))

for  (gato  en  myCats)  { print("$ para  (gato  en  myCats)  {
{gato?.nombre}:  ") si  (gato!  =  nulo)  {
¿gato?.  Miau() print("${gato?.nombre}:  ")  gato?.Miau()
}
Esto  compila,  pero  el  resultado  es  
} }
incorrecto  (se  imprime  nulo  para  el   } Esto  no  se  compila  porque  Cat  no  
} puede  tener  un  nombre  nulo.
segundo  elemento  en  la  matriz  myCats).

230  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Use  let  para  ejecutar  código  si  los  valores  no  son  nulos

Cuando  usa  tipos  anulables,  es  posible  que  desee  ejecutar  código  si  (y  solo  
si)  un  valor  particular  no  es  nulo.  Si  tienes  un  lobo?  variable  llamada  w,  por  
ejemplo,  es  posible  que  desee  imprimir  el  valor  de  la  propiedad  de  hambre  de  w  
siempre  que  w  no  sea  nulo.

Una  opción  para  realizar  este  tipo  de  tareas  es  usar  el  código:

si  (w !=  nulo ) {

println(con.hambre)

Esto  puede  suceder  si,  por  ejemplo,  w  define  una  propiedad  
Pero  si  el  compilador  no  puede  garantizar  que  la  variable  w  no  cambie   var  en  una  clase  y  desea  usar  su  propiedad  hambre  en  una  
entre  la  comprobación  de  nulos  y  su  uso,  el  código  no  se  compilará. función  separada.  Es  la  misma  situación  que  vio  anteriormente  
en  el  capítulo  cuando  presentamos  la  necesidad  de  llamadas  
seguras.
Un  enfoque  alternativo  que  funcionará  en  todas  las  situaciones  es  usar  el  
código:

w?.let  {

println(es.hambre) Si  w  no  es  nulo,  imprimamos  su  hambre.
}

Es  como  decir  “si  w  no  es  nulo,  imprimamos  su  hambre”.  Vamos  a  caminar  
a  través  de  esto.
?.let  le  permite  
La  palabra  clave  let  utilizada  junto  con  el  operador  de  llamada  segura ?.  le  dice  
al  compilador  que  desea  realizar  alguna  acción  cuando  el  valor  en  el  que  está  
ejecutar  código  para  
operando  no  es  nulo.  Entonces  el  siguiente  código:
un  valor  que  no  es  nulo.
w?.let  {

//  Codigo  para  hacer  algo

solo  ejecutará  el  código  en  su  cuerpo  si  w  no  es  nulo.

Una  vez  que  haya  establecido  que  el  valor  no  es  nulo,  puede  consultarlo  en  el  
cuerpo  del  let  usándolo .  Entonces,  en  el  siguiente  ejemplo  de  código,  se  refiere  
ÁRBITRO
a  una  versión  no  anulable  de  la  variable  w,  lo  que  le  permite  acceder  directamente  
hambre:  6
a  su  propiedad  de  hambre: w

w?.let  { Lobo
Puede  usar  "it"  para  acceder   var  Lobo?
println(es.hambre) directamente  a  las  funciones  y  
ÁRBITRO
“it”  es  una  versión  no  
} propiedades  de  Wolf.
anulable  de  w  que  hace  
él
Veamos  un  par  de  ejemplos  más  de  cuándo  usar  let  puede  ser  útil. referencia  al  mismo  objeto  Wolf.
Puede  referirse  a  "eso"  
var  lobo
dentro  del  cuerpo  let's.

231
estas  aqui  4
Machine Translated by Google

usando  let

Usando  let  con  elementos  de  matriz

let  también  se  puede  usar  para  realizar  acciones  usando  los  elementos  no  
nulos  de  una  matriz.  Puede  usar  el  siguiente  código,  por  ejemplo,  para  
recorrer  una  matriz  de  cadenas  e  imprimir  cada  elemento  que  no  sea  nulo:

var  array  =  arrayOf("Hola",  "Hola",  nulo)

para  (elemento  en  la  matriz)  {

artículo?.let  {

imprimir  (es) Esta  línea  solo  se  ejecuta  para  elementos  no  nulos  en  la  matriz

Uso  de  let  para  simplificar  expresiones
let  es  particularmente  útil  en  situaciones  en  las  que  desea  realizar  acciones  
en  el  valor  de  retorno  de  una  función  que  puede  ser  nulo.
Debe  usar  
Supongamos  que  tiene  una  función  llamada  getAlphaWolf  que  tiene  un  
llaves  para  
tipo  de  retorno  Wolf.  como  esto:
indicar  el  
divertido  getAlphaWolf() :  ¿Lobo?  {
cuerpo  let.

devolver  lobo()

} Si  omite  los  { },  su  código  no  se  
compilará.
Si  quisieras  obtener  una  referencia  al  valor  de  retorno  de  la  función  y  
llamar  a  su  función  eat  si  no  es  nula,  podrías  hacerlo  (en  la  mayoría  de  
las  situaciones)  usando  el  siguiente  código:

var  alfa  =  getAlphaWolf()

si  (alfa!  =  nulo)  {

alfa.comer()

Sin  embargo,  si  tuviera  que  reescribir  el  código  usando  let,  ya  no  
necesitaría  crear  una  variable  separada  en  la  que  almacenar  el  valor  de  
retorno  de  la  función.  En  su  lugar,  podrías  usar:

getAlphaWolf()?.let  { Usar  let  es  más  conciso.  También  es  seguro,  por  
eso.comer() lo  que  puede  usarlo  en  todas  las  situaciones.

Es  como  decir  “coge  el  lobo  alfa,  y  si  no  es  nulo,  que  se  lo  coma”.

232  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

En  lugar  de  usar  una  expresión  if...
Otra  cosa  que  puede  querer  hacer  cuando  tiene  tipos  anulables  es  usar  una  
expresión  if  que  especifique  un  valor  alternativo  para  algo  que  es  nulo.

Supongamos  que  tienes  un  Lobo?  variable  llamada  w,  como  antes,  y  desea  
utilizar  una  expresión  que  devuelva  el  valor  de  la  propiedad  de  hambre  de  w  si  
w  no  es  nulo,  pero  el  valor  predeterminado  es  ­1  si  w  es  nulo.  En  la  mayoría  de  
las  situaciones,  la  siguiente  expresión  funcionará:

if  (w !=  null)  w.hambre  else  ­1

Pero  como  antes,  si  el  compilador  cree  que  existe  la  posibilidad  de  que  la  
variable  w  se  haya  actualizado  entre  la  verificación  de  valores  nulos  y  su  uso,  
el  código  no  se  compilará  porque  el  compilador  considera  que  no  es  seguro.

[Nota  del  editor:  ¿Elvis?  ¿Esto  es  una  
Afortunadamente  existe  una  alternativa:  el  operador  Elvis.
broma?  Devolver  al  remitente.]
...puedes  usar  el  operador  Elvis  más  seguro
Muchas  
El  operador  de  Elvis ?:  es  una  alternativa  segura  a  una  expresión  if.  Se  llama   gracias.
el  operador  de  Elvis  porque  cuando  lo  inclinas  de  lado,  se  parece  un  poco  a   ?:
Elvis.

Aquí  hay  un  ejemplo  de  una  expresión  que  usa  un  operador  de  Elvis:

w?.hambre?:  ­1 Este  es  el  operador  de  Elvis.

El  operador  de  Elvis  primero  verifica  el  valor  a  su  izquierda,  en  este  caso:

w?.hambre

Si  este  valor  no  es  nulo,  el  operador  de  Elvis  lo  devuelve.  Sin  embargo,  si  el   El  operador  de  
valor  de  la  izquierda  es  nulo,  el  operador  de  Elvis  devuelve  el  valor  de  la   Elvis?:  es  una  versión  
derecha  (en  este  caso,  ­1).  Entonces  el  código

w?.hambre?:  ­1 segura  de  una  expresión  
es  como  decir  “si  w  no  es  nulo  y  su  propiedad  de  hambre  no  es  nula,   if.  Devuelve  el  valor  a  su  
devolver  el  valor  de  la  propiedad  de  hambre ,  de  lo  contrario  devolver  ­1”.  
Hace  lo  mismo  que  el  código: izquierda  si  no  es  nulo.
if  (w?.hunger !=  null)  w.hunger  else  ­1 De  lo  contrario,  
pero  debido  a  que  es  una  alternativa  más  segura,  puede  usarlo  en  cualquier  lugar.
devuelve  el  valor  a  su  derecha.
En  las  últimas  páginas,  ha  visto  cómo  acceder  a  las  propiedades  y  
funciones  de  un  tipo  anulable  mediante  llamadas  seguras,  y  cómo  usar  let  y  el  
operador  Elvis  en  lugar  de  declaraciones  y  expresiones  if.
Solo  hay  una  opción  más  que  queremos  mencionar  que  puede  usar  para  
verificar  valores  nulos:  el  operador  de  afirmación  no  nulo.

estas  aqui  4 233
Machine Translated by Google

NullPointerExceptions  intencionales

El !!  el  operador  lanza  deliberadamente  una  NullPointerException
El  operador  de  aserción  no  nulo,  o !!,  es  diferente  a  los  otros  métodos  
para  tratar  con  nulos  que  hemos  visto  en  las  últimas  páginas.  En  lugar  de  
asegurarse  de  que  su  código  sea  seguro  al  manejar  cualquier  valor  nulo,  el  
operador  de  aserción  no  nulo  lanza  deliberadamente  una  NullPointerException  
si  algo  resulta  ser  nulo.

Supongamos,  como  antes,  que  tienes  un  Lobo?  variable  llamada  w,  y  desea  
asignar  el  valor  de  su  propiedad  hambre  a  una  nueva  variable  llamada  x  si  w  o  
hambre  no  es  nulo.  Para  hacer  esto  usando  una  aserción  no  nula,  usaría  el  
siguiente  código:

var  x  =  w!!.hambre Aquí  el !!  hace  la  afirmación  de  que  w  no  es  nulo.

Si  w  y  el  hambre  no  son  nulos,  como  se  afirma,  el  valor  de  la  propiedad  
del  hambre  se  asigna  a  x.  Pero  si  w  o  hambre  es  nulo,  se  lanzará  una  
NullPointerException,  se  mostrará  un  mensaje  en  la  ventana  de  salida  del  IDE  y  
la  aplicación  dejará  de  ejecutarse.

El  mensaje  que  se  muestra  en  la  ventana  de  resultados  le  brinda  
información  sobre  NullPointerException,  incluido  un  seguimiento  de  pila  
que  le  brinda  la  ubicación  de  la  afirmación  no  nula  que  la  causó.  El  
siguiente  resultado,  por  ejemplo,  le  dice  que  NullPointerException  se  
lanzó  desde  la  función  principal  en  la  línea  45  en  el  archivo  App.kt: Aquí  está  NullPointerException,  con  un  
seguimiento  de  pila  que  le  indica  dónde  ocurrió.

Excepción  en  el  hilo  "principal"  kotlin.KotlinNullPointerException
en  AppKt.principal(App.kt:45) La  excepción  ocurrió  en  la  línea  45.

El  siguiente  resultado,  por  otro  lado,  le  dice  que  NullPointerException  
se  lanzó  desde  una  función  llamada  myFunction  en  la  clase  MyWolf  en  la  
línea  98  del  archivo  App.kt.  Esta  función  fue  llamada  desde  la  función  principal  
en  la  línea  67  del  mismo  archivo:

Excepción  en  el  hilo  "principal"  kotlin.KotlinNullPointerException
en  MyWolf.myFunction(App.kt:98)  en  
AppKt.main(App.kt:67)

Por  lo  tanto,  las  aserciones  no  nulas  son  útiles  si  desea  probar  suposiciones  
sobre  su  código,  ya  que  le  permiten  identificar  problemas.

Como  ha  visto,  el  compilador  de  Kotlin  hace  todo  lo  posible  para  asegurarse  de  
que  su  código  se  ejecute  sin  errores,  pero  todavía  hay  situaciones  en  las  que  
es  útil  saber  cómo  generar  excepciones  y  manejar  las  que  surjan.
Veremos  las  excepciones  después  de  mostrarle  el  código  completo  para  un  
nuevo  proyecto  que  trata  con  valores  nulos.

234  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Crear  el  proyecto  de  valores  nulos
Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asigne  al  
proyecto  el  nombre  "Valores  nulos".  Luego  cree  un  nuevo  archivo  de  
Kotlin  llamado  App.kt  resaltando  la  carpeta  src ,  haciendo  clic  en  el  menú  
Archivo  y  eligiendo  Nuevo  →  Archivo/Clase  de  Kotlin.  Cuando  se  le  solicite,  
nombre  el  archivo  "Aplicación"  y  elija  Archivo  en  la  opción  Tipo.

Agregaremos  varias  clases  y  funciones  al  proyecto,  y  una  función  
principal  que  las  usa,  para  que  pueda  explorar  cómo  funcionan  los  valores  
nulos.  Aquí  está  el  código:  actualice  su  versión  de  App.kt  para  que  coincida  
Estamos  usando  una  versión  
con  la  nuestra:
reducida  de  la  clase  Wolf  que  
usamos  en  capítulos  anteriores  
Crea  la  clase  Lobo. para  mantener  el  código  simple.

clase  lobo  {
Mi  lobo Lobo
var  hambre  =  10
lobo comida  para  
comida  val  =  "carne"
el  hambre

miFunción()
comer()
comer  divertido()  {

println("El  Lobo  esta  comiendo  $comida")

Cree  la  clase  MyWolf.
clase  Mi  Lobo  {

var  lobo:  Lobo?  =  Lobo()

Valores  nulos

diversión  myFunction()  {

¿lobo?.comer() origen

}
aplicación.kt
Cree  la  función  getAlphaWolf.

divertido  getAlphaWolf() :  ¿Lobo?  {

devolver  lobo()

El  código  continúa  en  
la  página  siguiente.

estas  aqui  4 235
Machine Translated by Google

prueba  de  manejo

El  código  continuó...
diversión  principal(argumentos:  Array<String>)  {
Mi  lobo Lobo
var  w:  ¿Lobo?  =  Lobo()
lobo comida  para  

el  hambre
si  (w !=  nulo)  {
miFunción()
comer() comer()

var  x  =  w?.hunger  println("El  
valor  de  x  es  $x")

Use  el  operador  Elvis  para  establecer  
var  y  =  w?.hunger ?:  ­1  println("El  valor  
y  en  el  valor  del  hambre  si  w  no  es  nulo.
de  y  es  $y")
Si  w  es  nulo,  establece  y  en  ­1.

var  myWolf  =  MyWolf()  
myWolf?.wolf?.hunger  =  8  println("El  
valor  de  myWolf?.wolf?.hunger  es  ${myWolf?.wolf?.hunger}")

var  myArray  =  arrayOf("Hola",  "Hola",  nulo)  for  (elemento  en  myArray)  
Valores  nulos
{
item?.let  { println(it) } Esto  imprime  los  elementos  
} no  nulos  en  la  matriz. origen

getAlphaWolf()?.let  { it.eat() }
aplicación.kt

w  =  nulo
Esto  arrojará  una  NullPointerException  ya  que  w  es  nulo.
var  z  =  w!!.hambre
}

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  
de  salida  del  IDE:
El  lobo  está  comiendo  carne.
el  valor  de  x  es  10
El  valor  de  y  es  10  El  
valor  de  myWolf?.wolf?.hunger  es  8  Hola

Hola
El  lobo  está  comiendo  carne  
Excepción  en  el  hilo  "principal"  kotlin.KotlinNullPointerException  en  
AppKt.main(App.kt:55)

236  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Rompecabezas  de  piscina

Su  trabajo  es  tomar  fragmentos  de  código
de  la  piscina  y  colocarlos pato  de  clase  (altura  de  val: =  nulo)  {
en  las  líneas  en  blanco  del  código.
graznido  divertido()  {
No  puede  usar  el  mismo  fragmento  
de  código  más  de  una  vez  y  no   println("¡Cuac!  ¡Cuac!")
necesitará  usar  todos  los  fragmentos   }
de  código.  Su  objetivo  es  crear  dos  
}
clases  llamadas

Pato  y  Mis  Patos.  MyDucks  debe  
contener  una  matriz  de  patos   class  MisPatos(var  misPatos:  Array< >)  {
anulables  e  incluir  funciones  para  
graznido  divertido()  {
hacer  que  cada  pato  grazne  y  
para  (pato  en  myDucks)  {
devolver  la  altura  total  de  todos  los  patos.
{

.curandero()

diversión  totalDuckHeight():  Int  {
var  h: =

para  (pato  en  myDucks)  {
h altura  del  pato 0

volver  h

Nota:  ¡cada  cosa  del  grupo   }

solo  se  puede  usar  una  vez!

pato
. +=
¿En  t? En  t

0 . él
Pato ? demás
¿En  t?
pato
= nulo ?: ?.
?.
¿Pato? hacer
En  t
dejar

estas  aqui  4 237
Machine Translated by Google

solución  de  rompecabezas  de  piscina

Esto  es  Int?,  no  Int,  ya  que  
Solución  de  rompecabezas  de  piscina debe  aceptar  un  valor  nulo.

Su  trabajo  es  tomar  fragmentos  de  código
de  la  piscina  y  colocarlos pato  de  clase  (altura  de  val: ¿En  t? =  nulo)  {
en  las  líneas  en  blanco  del  código.
graznido  divertido()  {
No  puede  usar  el  mismo  
fragmento  de  código  más  de  una  vez   println("¡Cuac!  ¡Cuac!")
y  no  necesitará  usar  todos  los   }
fragmentos  de  código.  Su  objetivo  es   myDucks  es  una  matriz  
} de  patos  anulables.
crear  dos  clases  llamadas
Pato  y  Mis  Patos.  MyDucks  debe  
contener  una  matriz  de  patos   class  MisPatos(var  misPatos:  Array<  Pato?  >)  {
anulables  e  incluir  funciones  para  
graznido  divertido()  {
hacer  que  cada  pato  grazne  y  
devolver  la  altura  total  de  todos  los  patos. para  (pato  en  myDucks)  {

pato ?. dejar {
Aquí,  estamos  usando  let  para  hacer  que  cada  pato  
él .curandero()
grazne,  pero  podríamos  haber  usado  pato?.cuack()  en  su  lugar.
}

diversión  totalDuckHeight():  Int  {
totalDuckHeight()  devuelve  un  Int,  por   var  h: = 0
En  t
lo  que  h  debe  ser  un  Int,  no  un  Int?.
para  (pato  en  myDucks)  {
Si  el  pato  y  su  altura  no  son  nulos,  suma  la  altura  del  pato  a   h += ?.  altura  del  pato ?: 0
h.  De  lo  contrario,  agregue  0  a  h  en  su  lugar. }

volver  h

No  necesitabas  usar  

estos  fragmentos.

.
En  t

.
Pato ? demás
¿En  t?
pato
= nulo
hacer

238  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Se  lanza  una  excepción  en  circunstancias  excepcionales.
Como  dijimos  anteriormente,  una  excepción  es  un  tipo  de  advertencia  
sobre  situaciones  excepcionales  que  aparecen  en  tiempo  de  ejecución.  Es  
una  forma  de  que  el  código  diga  "Algo  malo  pasó,  fallé".

Suponga,  por  ejemplo,  que  tiene  una  función  llamada  myFunction  
que  convierte  un  parámetro  String  en  un  Int  y  lo  imprime:

fun  myFunction(str:  String)  {

val  x  =  str.toInt()

imprimir(x)

println("miFuncion  ha  finalizado")

Si  pasa  una  Cadena  como  "5"  a  myFunction,  el  código  convertirá  con  éxito  la  
Cadena  en  un  Int  e  imprimirá  el  valor  5,  junto  con  el  texto  "myFunction  ha  
finalizado".  Sin  embargo,  si  le  pasa  a  la  función  una  cadena  que  no  se  puede  
convertir  en  un  entero,  como  "Soy  un  nombre,  no  un  número",  el  código  dejará  
de  ejecutarse  y  mostrará  un  mensaje  de  excepción  como  este:
¡Ay!
Excepción  en  el  subproceso  "principal"  java.lang.NumberFormatException:  para  la  cadena  de  entrada:  "Soy  un  nombre,  no  un  número"
en  java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

en  java.lang.Integer.parseInt(Integer.java:580) El  seguimiento  de  la  pila  
de  excepciones  menciona  
en  java.lang.Integer.parseInt(Integer.java:615)
Java  porque  estamos  
en  AppKt.myFunction(App.kt:119)  en  
ejecutando  nuestro  código  en  la  JVM.
AppKt.main(App.kt:3)

Puede  detectar  las  excepciones  que  se  lanzan
Cuando  se  lanza  una  excepción,  tiene  dos  opciones  para  tratarla:

¥ Puede  dejar  la  excepción  en  paz.
Esto  mostrará  un  mensaje  en  la  ventana  de  salida  y  detendrá  su  aplicación  
(como  se  indicó  anteriormente).

¥ Puede  capturar  la  excepción  y  manejarla.
Si  sabe  que  puede  obtener  una  excepción  cuando  ejecuta  determinadas  líneas  de  
código,  puede  prepararse  para  ello  y  posiblemente  recuperarse  de  lo  que  sea  que  lo  
haya  causado.

Ha  visto  lo  que  sucede  cuando  deja  las  excepciones  en  paz,  así  que  veamos  
cómo  las  detecta.

estas  aqui  4 239
Machine Translated by Google

tratar  de  atrapar

Detectar  excepciones  usando  un  intento/captura
Voy  a  PROBAR  esta  
Las  excepciones  se  detectan  envolviendo  el  código  de  riesgo  en  un  bloque   cosa  arriesgada  y  me  
de  prueba/captura .  Un  bloque  try/catch  le  dice  al  compilador  que  usted   ATRAPARÉ  si  fallo.
sabe  que  podría  suceder  algo  excepcional  en  el  código  que  desea  ejecutar  
y  que  está  preparado  para  manejarlo.  Al  compilador  no  le  importa  cómo  lo  
manejes;  sólo  le  importa  que  digas  que  te  estás  ocupando  de  ello.

Así  es  como  se  ve  un  bloque  de  prueba/captura:

fun  myFunction(str:  String)  {

Aquí  está  el  intento... intentar  {

val  x  =  str.toInt()

imprimir(x)
...  y  aquí   }  captura  (e:  NumberFormatException)  {
está  el  truco. println("Lamentable")

println("miFuncion  ha  finalizado")

La  parte  try  del  bloque  try/catch  contiene  el  código  arriesgado  que  podría  
causar  una  excepción.  En  el  ejemplo  anterior,  este  es  el  código:

intentar  {

val  x  =  str.toInt()

imprimir(x)

La  parte  de  captura  del  bloque  especifica  la  excepción  que  desea  capturar  e  
incluye  el  código  que  desea  ejecutar  si  la  detecta.  Entonces,  si  nuestro  
código  arriesgado  arroja  una  NumberFormatException,  la  atraparemos  e  
imprimiremos  un  mensaje  significativo  como  este:

captura  (e:  NumberFormatException)  {

println("Lamentable")
Esta  línea  solo  se  ejecutará  si  se  detecta  una  excepción.
}

Cualquier  código  que  sigue  al  bloque  catch  se  ejecuta,  en  este  caso  el  código:

println("miFuncion  ha  finalizado")

240  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Use  finalmente  para  las  
cosas  que  quiere  hacer  sin  importar  qué
Si  tiene  un  código  de  limpieza  importante  que  desea  ejecutar  
independientemente  de  una  excepción,  puede  colocarlo  en  un  bloque  finalmente .
El  bloque  finalmente  es  opcional,  pero  está  garantizado  que  se  ejecutará  
pase  lo  que  pase.

Para  ver  cómo  funciona  esto,  supón  que  quieres  hornear  algo  
experimental  que  podría  salir  mal.

Empiezas  por  encender  el  horno.
probar/atrapar/
Si  lo  que  intentas  cocinar  tiene  éxito,  tienes  que  apagar  el  horno.
finalmente  control  de  flujo

Si  lo  que  intenta  es  un  completo  fracaso,  debe  apagar  el  horno.
¥  Si  el  bloque  try  falla  (una  
excepción):  el  control  de  flujo  se  
Tienes  que  apagar  el  horno  pase  lo  que  pase,  por  lo  que  el  código  para   mueve  inmediatamente  al  bloque  
catch .  Cuando  se  completa  el  bloque  
apagar  el  horno  pertenece  a  un  bloque  finalmente:
catch ,  se  ejecuta  finalmente  el  bloque.  Cuando  
intentar  { finaliza  el  bloque ,  continúa  el  resto  del  código.
encender  el  horno  ()

x.hornear()

}  catch  (e:  Excepción  para  hornear)  {
¥  Si  el  bloque  de  prueba  tiene  éxito  (sin  
println("Experimento  de  horneado  fallido") excepción):  el  control  de  flujo  salta  el  bloque  
}  finalmente  { de  captura  y  se  mueve  al  bloque  de  finalización .
Siempre  queremos  llamar  
apagar  el  horno  () a  turnOvenOff(),  por  lo  que  
Cuando  finaliza  el  bloque ,  continúa  el  resto  del  
} pertenece  al  bloque  finalmente. código.

Sin  finalmente,  tienes  que  poner  la  llamada  a  la  función  turnOvenOff  
¥  Si  el  bloque  try  o  catch  tiene  una  declaración  
tanto  en  el  intento  como  en  la  captura  porque  tienes  que  apagar  el  
de  retorno,  finalmente  se  ejecutará:  el  flujo  
horno  pase  lo  que  pase.  Un  bloque  finalmente  te  permite  poner  todo  tu   salta  al  bloque  finalmente  y  luego  vuelve  al  
código  de  limpieza  importante  en  un  solo  lugar,  en  lugar  de  duplicarlo  así:
retorno.

intentar  {

encender  el  horno  ()

x.hornear()

apagar  el  horno  ()

}  catch  (e:  Excepción  para  hornear)  {

println("Experimento  de  horneado  fallido")

apagar  el  horno  ()

estas  aqui  4 241
Machine Translated by Google

jerarquía  de  excepción

Una  excepción  es  un  objeto  de  tipo  Excepción
¡Soy  
Cada  excepción  es  un  objeto  de  tipo  Excepción.  Es  la  superclase  
de  todas  las  excepciones,  por  lo  que  todo  tipo  de  excepción  hereda  de   excepcional!

ella.  En  la  JVM,  por  ejemplo,  cada  excepción  tiene  una  función  llamada  
printStackTrace  que  puede  usar  para  imprimir  el  seguimiento  de  la  pila  
de  la  excepción  usando  un  código  como  este:

intentar  { printStackTrace()  es  una  función  que  está  disponible  
//  Haz  algo  arriesgado para  todas  las  excepciones  que  se  ejecutan  en  la  JVM.  
Excepción
Si  no  puede  recuperarse  de  una  excepción,  use  
}  catch  (e:  Excepción)  {
printStackTrace()  para  ayudarlo  a  localizar  la  causa  del  problema.
e.printStackTrace()

//Otro  código  que  se  ejecuta  cuando  obtiene  una  excepción

}
Throwable  es  la  superclase  de  Exception.

Hay  muchos  tipos  diferentes  de  excepción,  cada  uno  de  los  cuales  es  un  
subtipo  de  excepción.  Algunos  de  los  más  comunes  (o  famosos)  son:
arrojable

causa
mensaje
¥ NullPointerException  Se  
lanza  cuando  intenta  realizar  operaciones  en  un  valor  nulo.  Como  ha  
visto,  las  NullPointerExceptions  están  casi  extintas  en  Kotlinville.

¥ ClassCastException   Excepción
Obtendrá  esto  si  intenta  convertir  un  objeto  en  un  tipo  incorrecto,  como  
convertir  un  lobo  en  un  árbol.

¥ IllegalArgumentException  Puede  
lanzar  esto  si  se  ha  pasado  un  argumento  ilegal.

¥ IllegalStateException  Use   Otra  excepción
esto  si  algún  objeto  tiene  un  estado  que  no  es  válido.

También  puede  crear  sus  propios  tipos  de  excepción  definiendo  una  
nueva  clase  con  Exception  como  su  superclase.  El  siguiente  código,  
por  ejemplo,  define  un  nuevo  tipo  de  excepción  llamado   Cada  excepción  es  una  
AnimalException: subclase  de  Excepción,  

clase  AnimalException :  Excepción()  { } incluidas  todas  las  
mencionadas  en  esta  página.
Definir  sus  propios  tipos  de  excepción  a  veces  puede  ser  útil  si  desea  
generar  excepciones  deliberadamente  en  su  propio  código.
Veremos  cómo  se  hace  esto  después  de  una  pequeña  desviación.

242  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Lanzamientos  seguros  de  cerca

Como  aprendió  en  el  Capítulo  6,  en  la  mayoría  de  las  circunstancias,  el  compilador  
realizará  una  conversión  inteligente  cada  vez  que  use  el  operador  is.  En  el  siguiente  
código,  por  ejemplo,  el  compilador  verifica  si  la  variable  r  contiene  un  objeto  Wolf,  por  lo  que  
(interfaz)
puede  convertir  inteligentemente  la  variable  de  Roamable  a  Wolf: itinerante

val  r:  Roamable  =  Wolf()
si  (r  es  lobo)  {
comer() Aquí,  r  ha  sido  moldeado  inteligentemente  a  un  Lobo.

En  algunas  situaciones,  el  compilador  no  puede  realizar  una  conversión  inteligente,  ya  que  
Animal
la  variable  puede  cambiar  entre  la  comprobación  de  su  tipo  y  su  uso.  El  siguiente  código,  
por  ejemplo,  no  se  compilará  porque  el  compilador  no  puede  estar  seguro  de  que  la   hambre
propiedad  r  siga  siendo  Wolf  después  de  verificarla:
comer()

clase  MiRoamable  {
var  r:  Roamable  =  Wolf()

Lobo
diversión  myFunction()  {
si  (r  es  lobo)  {
comer() Esto  no  compilará,  porque  el  compilador   comer()

} no  puede  garantizar  que  r  todavía  contenga  una  

} referencia  a  un  objeto  Wolf.

Viste  en  el  Capítulo  6  que  puedes  lidiar  con  esto  usando  la  palabra  clave  as  para  convertir  
explícitamente  a  r  como  un  lobo  de  esta  manera:
Esto  se  compilará,  pero  si  r  ya  no  contiene  
si  (r  es  lobo)  {
una  referencia  a  un  objeto  Wolf,  obtendrá  
val  lobo  =  r  como  lobo
una  excepción  en  tiempo  de  ejecución.
lobo.comer()
}

Pero  si  a  r  se  le  asigna  un  valor  de  algún  otro  tipo  entre  la  verificación  de  tipo  y  la  
¿como?  le  
conversión,  el  sistema  generará  una  ClassCastException.
permite  realizar  un  
La  alternativa  segura  es  realizar  un  lanzamiento  seguro  usando  el  as?  operador  usando  
un  código  como  este: lanzamiento  explícito  
val  lobo  =  r  como?  Lobo seguro.  Si  la  
Esto  proyecta  r  como  un  lobo  si  r  tiene  un  objeto  de  ese  tipo  y  devuelve  nulo  si  no  lo  tiene.   conversión  falla,  devuelve  nulo.
Esto  le  evita  obtener  una  ClassCastException  si  sus  suposiciones  sobre  el  tipo  de  variable  
son  incorrectas.

243
estas  aqui  4
Machine Translated by Google

como  lanzar  excepciones

Puede  lanzar  excepciones  explícitamente
A  veces  puede  ser  útil  lanzar  deliberadamente  excepciones  en  su  
propio  código.  Si  tiene  una  función  llamada  setWorkRatePercentage,  
por  ejemplo,  es  posible  que  desee  lanzar  una  excepción  
IllegalArgumentException  si  alguien  intenta  establecer  un  porcentaje  que  sea  
menor  que  0  o  mayor  que  100.  Hacerlo  obliga  a  la  persona  que  llama  a  abordar  
el  problema,  en  lugar  de  confiar  en  la  función.  para  decidir  qué  hacer.

Lanzas  una  excepción  usando  la  palabra  clave  throw .  Así  es  como,  por  
ejemplo,  obtendría  la  función  setWorkRatePercentage  para  lanzar  una  
IllegalArgumentException:

diversión  setWorkRatePercentage(x:  Int)  { Esto  lanza  una  IllegalArgumentException  si  x  no  
está  en  el  rango  0..100
si  (x!  en  0..100)  {

throw  IllegalArgumentException("El  porcentaje  no  está  en  el  rango  0..100:  $x")
}

//Más  código  que  se  ejecuta  si  el  argumento  es  válido
}

Luego  podría  capturar  la  excepción  usando  un  código  como  este:

intentar  {

setWorkRatePercentage(110) La  función  setWorkRatePercentage()  no  puede  
hacer  que  nadie  trabaje  al  110%,  por  lo  que  la  
}  catch(e:  excepción  de  argumento  ilegal)  {
persona  que  llama  tiene  que  lidiar  con  el  problema.
//Código  para  manejar  la  excepción
}

Reglas  de  excepción

¥  No  puedes  tener  una  captura  o  finalmente  sin   ¥  Un  intento  debe  ser  seguido  por  una  captura  o  un  
intentarlo. No  es  legal  ya   finalmente.
que  no  hay  intento. Legal  porque  hay  un   prueba  { callRiskyCode() }
callRiskyCode()

captura  (e:  BadException)  { } finalmente,  aunque  no  hay   finalmente  { }


trampa.

¥  No  se  puede  poner  código  entre  el  intento  y  la  captura,  
¥  Un  intento  puede  tener  varios  bloques  catch .
o  la  captura  y  el  finalmente.

No  es  legal  ya  que   Legal  porque   prueba  { callRiskyCode() }


prueba  { callRiskyCode() }
no  puede  poner   un  intento  puede   captura  (e:  BadException)  { }
x  =  7
código  entre  el   tener  más  de  
captura  (e:  ScaryException)  { }
intento  y  la  captura. captura  (e:  BadException)  { } una  captura.

244  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

probar  y  tirar  son  ambas  expresiones
A  diferencia  de  otros  lenguajes  como  Java,  try  y  throw  son  expresiones,  por  lo  
que  pueden  tener  valores  de  retorno.

Cómo  usar  try  como  una  expresión
El  valor  de  retorno  de  un  intento  es  la  última  expresión  en  el  intento  o  la  última  
expresión  en  la  captura  (el  bloque  finalmente,  si  existe,  no  afecta  el  valor  de  
Esto  es  como  decir  "Intente  
retorno).  Considere  el  siguiente  código,  por  ejemplo:
asignar  str.toInt()  al  resultado,  
val  result  =  try  { str.toInt() }  catch  (e:  Exception)  { null }
pero  si  no  puede,  establezca  
el  resultado  en  nulo".
El  código  crea  una  variable  denominada  resultado  de  tipo  Int?.  El  bloque  try  
intenta  convertir  el  valor  de  una  variable  String  llamada  str  en  un  Int.  Si  esto  
tiene  éxito,  asigna  el  valor  Int  al  resultado.  Sin  embargo,  si  el  bloque  de  prueba  
falla,  asigna  nulo  al  resultado  en  su  lugar:

Cómo  usar  throw  como  una  expresión
throw  también  es  una  expresión,  por  lo  que  puede,  por  ejemplo,  usarla  con  el
Operador  de  Elvis  usando  un  código  como  este:

val  h  =  w?.  ¿hambre?:  throw  AnimalException()

Si  w  y  el  hambre  no  son  nulos,  el  código  anterior  asigna  el  valor  de  la  
propiedad  hambre  de  w  a  una  nueva  variable  llamada  h.  Sin  embargo,  si  w  o  
el  hambre  son  nulos,  lanza  una  AnimalException.

P:  Dijiste  que  puedes  usar  throw P:  Lo  entiendo.  Nada  es  un  tipo  que P:  En  Java  tengo  que  declarar  cuando  un


en  una  expresión.  ¿Eso  significa   no  tiene  valores.  ¿Hay  algo  para  lo   método  lanza  una  excepción.
que  throw  tiene  un  tipo?  ¿Qué  es? que  quiera  usar  ese  tipo?
R:  Eso  es  correcto,  pero  no  en
R:  También  puedes  usar  Nada  para
Kotlin.  Kotlin  no  diferencia  entre
A:  throw  tiene  un  tipo  de  retorno  de
Nada.  Este  es  un  tipo  especial  que  no  
denotan  ubicaciones  de  código  que  nunca  pueden  ser excepciones  marcadas  y  no  marcadas.

tiene  valores,  entonces,  ¿una  variable  de   alcanzó.  Puede,  digamos,  usarlo  como  el  tipo  de  

tipo  Nothing?  solo  puede  contener  un  valor  nulo . retorno  de  una  función  que  nunca  regresa:

El  siguiente  código,  por  ejemplo,  crea  
una  variable  denominada  x  de  tipo   fun  fail():  Nada  {
Nothing?  eso  solo  puede  ser  nulo: lanzar  BadException()
}
var  x  =  nulo
El  compilador  sabe  que  el  código  detiene  
la  ejecución  después  de  llamar  a  fail() .

estas  aqui  4 245
Machine Translated by Google

afilar

Mira  el  código  de  la  izquierda.  ¿Cuál  crees  que  será  la  salida  cuando  se  
ejecute?  ¿Qué  crees  que  sería  si  el  código  de  la  línea  2  se  cambiara  por  el  
siguiente?:

prueba  val:  Cadena  =  "Sí"

Escribe  tus  respuestas  en  los  recuadros  de  la  derecha.

diversión  principal(argumentos:  Array<String>)  {
Salida  cuando  prueba  =  "No"
prueba  val:  Cadena  =  "No"

intentar  {

println("Iniciar  prueba")

código  de  riesgo  (prueba)

println("Terminar  intento")

}  captura  (e:  BadException)  {

println("Excepción  incorrecta")

}  finalmente  {

println("Finalmente")
}

println("Fin  de  principal")
Salida  cuando  prueba  =  "Sí"
}

clase  BadException:  excepción  ()

código  de  riesgo  divertido  (prueba:  cadena)  {

println("Iniciar  código  arriesgado")

si  (prueba  ==  "Sí")  {

lanzar  BadException()
}

println("Finalizar  código  riesgoso")
}

Respuestas  en  la  página  248.

246  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Imanes  de  código
Algo  de  código  Kotlin  está  revuelto  en  el  refrigerador.  Vea  si  
puede  reconstruir  el  código  para  que  si  a  myFunction  se  le  pasa  
una  cadena  de  "Sí",  imprima  el  texto  "descongela",  y  si  a  myFunction  
se  le  pasa  una  cadena  de  "No",  imprima  el  texto  "throws".
Los  imanes  deben  ir  en  este  espacio.

código  de  riesgo  divertido  (prueba:  cadena)  {

imprimir("h") }  finalmente  {

clase  BadException:  excepción  ()

fun  myFunction(prueba:  Cadena)  {

si  (prueba  ==  "Sí")  {

lanzar  BadException()

imprimir("w") código  de  riesgo  (prueba)

imprimir("t")
intentar  {

}
imprimir("a")

huellas  dactilares")
imprimir("o")

imprimir("r")
}

}  captura  (e:  BadException)  {
}

Respuestas  en  la  página  249.

estas  aqui  4 247
Machine Translated by Google

afilar  solución

Mira  el  código  de  la  izquierda.  ¿Cuál  crees  que  será  la  salida  cuando  se  
ejecute?  ¿Qué  crees  que  sería  si  el  código  de  la  línea  2  se  cambiara  por  el  
siguiente?:

prueba  val:  Cadena  =  "Sí"

Escribe  tus  respuestas  en  los  recuadros  de  la  derecha.

diversión  principal(argumentos:  Array<String>)  {
Salida  cuando  prueba  =  "No"
prueba  val:  Cadena  =  "No"

Empezar  a  probar
intentar  {

println("Iniciar  prueba") Iniciar  código  arriesgado

código  de  riesgo  (prueba)
Terminar  con  el  código  riesgoso
println("Terminar  intento")

}  captura  (e:  BadException)  { Finalizar  intento

println("Excepción  incorrecta")
Finalmente
}  finalmente  {

println("Finalmente")
fin  de  principal

println("Fin  de  principal")
Salida  cuando  prueba  =  "Sí"
}

Empezar  a  probar
clase  BadException:  excepción  ()

Iniciar  código  arriesgado

código  de  riesgo  divertido  (prueba:  cadena)  {
mala  excepción
println("Iniciar  código  arriesgado")

Finalmente

si  (prueba  ==  "Sí")  {
fin  de  principal
lanzar  BadException()
}

println("Finalizar  código  riesgoso")
}

248  Capítulo  8
Machine Translated by Google

nulos  y  excepciones

Solución  de  imanes  de  código
Algo  de  código  Kotlin  está  revuelto  en  el  refrigerador.  Vea  si  
puede  reconstruir  el  código  para  que  si  a  myFunction  se  le  pasa  
una  cadena  de  "Sí",  imprima  el  texto  "descongela",  y  si  a  myFunction  
se  le  pasa  una  cadena  de  "No",  imprima  el  texto  "throws".

Cree  una  subclase  de  Excepción.
clase  BadException:  excepción  ()

fun  myFunction(prueba:  Cadena)  { Crear  miFunción.

intentar  {

imprimir("t")
Intenta  ejecutar  este  código.
código  de  riesgo  (prueba)

imprimir("o")

}  captura  (e:  BadException)  {

imprimir("a")
Ejecute  este  código  si  se  lanza  una  BadException.

}  finalmente  {

imprimir("w") Este  código  se  ejecuta  pase  lo  que  pase.

huellas  dactilares")

Crear  código  de  riesgo
código  de  riesgo  divertido  (prueba:  cadena)  {

imprimir("h")

si  (prueba  ==  "Sí")  {

lanzar  BadException() Lanzar  una  BadException  si  la  prueba  ==  "Sí"

imprimir("r")

estas  aqui  4 249
Machine Translated by Google

caja  de  herramientas

Tu  caja  de  herramientas  de  Kotlin

Puede  descargar  
Tiene  el  Capítulo  8  en  su  haber  y  ahora  ha  agregado  
el  código  completo  
valores  nulos  y  excepciones  a  su  caja  de  
del  capítulo  desde  
herramientas.
CAPÍTULO  
8 https://tinyurl.com/
HFKotlin.

null  es  un  valor  que  significa  que  una  variable  no   El  operador  Elvis  (?:)  es  una  alternativa  
contiene  una  referencia  a  un  objeto. segura  a  una  expresión  if.
La  variable  existe,  pero  no  hace  referencia  a  nada.
El  operador  de  aserción  no  nulo  (!!)  lanza  una  
NullPointerException  si  el  asunto  de  su  aserción  es  nulo.
Un  tipo  que  acepta  valores  NULL  puede  contener  

valores  NULL  además  de  su  tipo  base.  Usted  define  un  
Una  excepción  es  una  advertencia  que  se  produce  
tipo  como  anulable  agregando  un ?  hasta  el  final  de  la  misma.
en  situaciones  excepcionales.  Es  un  objeto  de  tipo  
Para  acceder  a  las  propiedades  y  funciones  de  una   Excepción.
variable  anulable,  primero  debe  verificar  que  no  sea  
Usa  throw  para  lanzar  una  excepción.
nula.

Captura  una  excepción  usando  try/catch/finally.
Si  el  compilador  no  puede  garantizar  que  una  
variable  no  sea  nula  entre  una  comprobación  nula  

y  su  uso,  debe  acceder  a  las  propiedades  y   try  and  throw  son  expresiones.
funciones  mediante  el  operador  de  llamada  segura  (?.).
Use  un  lanzamiento  seguro  (¿como?)  para  evitar  
obtener  una  ClassCastException.
Puede  encadenar  llamadas  seguras  juntas.

Para  ejecutar  código  si  (y  solo  si)  un  valor  no  es  
nulo,  use ?.let.

250  Capítulo  8
Machine Translated by Google

9 colecciones

Conseguir
Organizado
Oh,  si  tan  solo  hubiera  
una  manera  de  agregar  
nuevos  novios  a  mi  colección...

¿Alguna  vez  quiso  algo  más  flexible  que  una  matriz?
Kotlin  viene  con  un  montón  de  colecciones  útiles  que  le  brindan  más  flexibilidad  y  un  mayor  
control  sobre  cómo  almacena  y  administra  grupos  de  objetos.  ¿ Quiere  mantener  una  lista  
redimensionable  a  la  que  pueda  seguir  agregando?  ¿Quiere  ordenar,  barajar  o  invertir  su  
contenido?  ¿ Quieres  encontrar  algo  por  nombre?  ¿O  quieres  algo  que  elimine  automáticamente  
los  duplicados  sin  que  muevas  un  dedo?  Si  quieres  alguna  de  estas  cosas,  o  más,  sigue  
leyendo.  esta  todo  aqui...

este  es  un  nuevo  capitulo  251
Machine Translated by Google

más  sobre  arreglos

Las  matrices  pueden  ser  útiles...

Hasta  ahora,  cada  vez  que  queríamos  tener  referencias  a  
un  grupo  de  objetos  en  un  solo  lugar,  usamos  una  matriz.  Las  
matrices  se  crean  rápidamente  y  tienen  muchas  funciones  útiles.   1 3 2
Estas  son  algunas  de  las  cosas  que  puede  hacer  con  una  matriz  
(según  el  tipo  de  sus  elementos):
ÁRBITRO ÁRBITRO ÁRBITRO

¥ Haz  una  matriz:

var  matriz  =  matrizDe(1,  3,  2)

¥ Haz  una  matriz  inicializada  con  valores  nulos: Crea  una  matriz  de  tamaño  2  inicializada  
con  valores  nulos.  Es  como  decir:  
var  nullArray:  Array<String?>  =  arrayOfNulls(2) arrayOf(null,  null)

¥ Averigüe  el  tamaño  de  la  matriz:
2 3 1
matriz  tiene  espacio  para  tres  
val  tamaño  =  matriz.tamaño elementos,  por  lo  que  su  tamaño  es  3.

¥ Invierta  el  orden  de  los  elementos  en  la  matriz: ÁRBITRO ÁRBITRO ÁRBITRO

Cambia  el  orden  de  los  elementos  de  la  matriz.
array.reverse()

¥ Averigua  si  contiene  algo:

val  isIn  =  array.contains(1) matriz  contiene  1,  por  lo  que  devuelve  verdadero.

¥ Calcula  la  suma  de  sus  elementos  (si  son  numéricos):
Esto  devuelve  6  como  2  +  3  +  1  =  6.
val  suma  =  array.sum()

¥ Calcule  el  promedio  de  sus  artículos  (si  son  numéricos):

val  promedio  =  matriz.promedio() Esto  devuelve  un  Doble,  en  este  caso,  (2  +  3  +  1)/3  =  2,0.

¥ Averigüe  el  artículo  mínimo  o  máximo  (funciona  para  números,
cadenas,  caracteres  y  booleanos):
min()  devuelve  1,  ya  que  este  es  el  valor  más  bajo  de  la  
matriz.min()
matriz.  max()  devuelve  3  ya  que  este  es  el  más  alto.
matriz.max()
1 2 3

¥ Ordene  la  matriz  en  un  orden  natural  (funciona  para  números,
cadenas,  caracteres  y  booleanos):
ÁRBITRO ÁRBITRO ÁRBITRO

Cambia  el  orden  de  los  elementos  de  la  matriz  
array.ordenar()
para  que  vayan  del  valor  más  bajo  al  más  alto,  de  
o falso  a  verdadero.
Pero  las  matrices  no  son  perfectas.

252  Capítulo  9
Machine Translated by Google

colecciones

...pero  hay  cosas  que  una  matriz  no  puede  manejar
Aunque  una  matriz  le  permite  realizar  muchas  acciones  útiles,  hay  dos  
áreas  importantes  en  las  que  las  matrices  se  quedan  cortas.

No  puedes  cambiar  el  tamaño  de  una  matriz

Cuando  crea  una  matriz,  el  compilador  deduce  su  tamaño  a  partir  de  la  
cantidad  de  elementos  con  los  que  se  inicializa.  Su  tamaño  se  fija  
entonces  para  siempre.  La  matriz  no  crecerá  si  desea  agregarle  un  nuevo  
elemento,  y  no  se  reducirá  si  desea  eliminar  un  elemento.

Las  matrices  son  mutables,  por  lo  que  se  pueden  actualizar

Otra  limitación  es  que  una  vez  que  crea  una  matriz,  no  puede  evitar  que  
se  modifique.  Si  crea  una  matriz  usando  un  código  como  este:

val  miArray  =  arrayOf(1,  2,  3)

no  hay  nada  que  impida  que  la  matriz  se  actualice  de  esta  manera:

miArray[0]  =  6

Si  su  código  se  basa  en  que  la  matriz  no  cambia,  esto  puede  ser  una  
fuente  de  errores  en  su  aplicación.

Entonces,  ¿cuál  es  la  alternativa?

P:  ¿No  puedo  eliminar  un  elemento  de  una  matriz  configurándolo  en P:  ¿No  podría  crear  una  copia  de  la  matriz  que  tenga  un  diferente
¿tamaño?
¿nulo?

R:  Si  crea  una  matriz  que  contiene  tipos  anulables,  puede  establecer R:  Podría,  y  las  matrices  incluso  tienen  una  función  llamada  más
uno  o  más  de  sus  elementos  a  nulo  usando  un  código  como  este: eso  lo  hace  más  fácil;  plus  copia  la  matriz  y  agrega  un  nuevo  elemento  
al  final  de  la  misma.  Pero  esto  no  cambia  el  tamaño  de  la  matriz  original.

val  a:  Array<Int?>  =  arrayOf(1,  2,  3)  a[2]  =  null
P:  ¿ Es  eso  un  problema?

Sin  embargo,  esto  no  cambia  el  tamaño  de  la  matriz.  En  el  ejemplo  
R:  Sí.  Necesitarás  escribir  código  extra,  y  si  otras  variables
anterior,  el  tamaño  de  la  matriz  sigue  siendo  3  aunque  uno  de  sus  elementos  
mantenga  referencias  a  la  versión  anterior  de  la  matriz,  esto  podría  conducir  a  un  
se  haya  establecido  en  nulo.
código  con  errores.

Sin  embargo,  existen  buenas  alternativas  al  uso  de  una  matriz,  que  veremos  
a  continuación.

estas  aqui  4 253
Machine Translated by Google

biblioteca  estándar  de  kotlin

En  caso  de  duda,  acude  a  la  Biblioteca
Biblioteca  estándar
Kotlin  se  envía  con  cientos  de  clases  y  funciones  prediseñadas  que  puede  
usar  en  su  código.  Ya  conoces  algunos  de  estos,  como  String  y  Any.  Y  la   Puedes  ver  lo  que  hay  en  el  Kotlin
gran  noticia  para  nosotros  es  que  la  biblioteca  estándar  de  Kotlin  incluye   navegando
Biblioteca  estándar  
clases  que  brindan  excelentes  alternativas  a  las  matrices. a:
En  la  biblioteca  estándar  de  Kotlin,  las  clases  y  funciones  se  agrupan  en  
paquetes.  Cada  clase  pertenece  a  un  paquete  y  cada  paquete  tiene  un   https://kotlinlang.org/api/latest/jvm/stdlib/index.html
nombre.  El  paquete  kotlin ,  por  ejemplo,  contiene  funciones  y  tipos  
básicos,  y  el  paquete  kotlin.math  contiene  funciones  y  constantes  matemáticas.

El  paquete  que  nos  interesa  aquí  es  el  paquete  kotlin.collections .  Este  
paquete  incluye  varias  clases  que  le  permiten  agrupar  objetos  en  una  
colección.  Veamos  los  principales  tipos  de  colección.

Puede  usar  
estos  filtros  

para  mostrar  solo  
aquellas  colecciones  
que  son  relevantes  
para  una  plataforma  
en  particular  o  una  
versión  de  Kotlin.

Aquí  está  el  kotlin.  

paquete  de  colecciones  
en  la  biblioteca  

estándar  de  Kotlin.

254  Capítulo  9
Machine Translated by Google

colecciones

Lista,  conjunto  y  mapa
Kotlin  tiene  tres  tipos  principales  de  colección:  Lista,  Conjunto  y  Mapa,  
ÁRBITRO "Té"
y  cada  uno  tiene  su  propio  propósito:
0

Cadena
Lista  ­  cuando  la  secuencia  importa ÁRBITRO

1
Una  lista  conoce  y  se  preocupa  por  la  posición  del  índice.  Sabe  dónde   "Café"
está  algo  en  la  Lista  y  puede  tener  más  de  un  elemento  que  haga  
ÁRBITRO
Una  lista  permite  
referencia  al  mismo  objeto. 2 Cadena
valores  duplicados.

Lista

"Jim"
Conjunto:  cuando  la  singularidad  importa
Un  conjunto  no  permite  
Un  conjunto  no  permite  duplicados  y  no  se  preocupa  por  el  orden  en   Cadena
ÁRBITRO
valores  duplicados.
que  se  mantienen  los  valores.  Nunca  puede  tener  más  de  un  elemento  
ÁRBITRO
que  haga  referencia  al  mismo  objeto,  o  más  de  un  elemento  que  haga   "Demandar"

referencia  a  dos  objetos  que  se  consideran  iguales.

Cadena
Colocar

Mapa:  al  encontrar  algo   “ValorA” “ValorB”

por  asuntos  clave

Un  mapa  utiliza  pares  clave/valor.  Conoce  el  valor  
asociado  a  una  clave  dada.  Puede  tener  dos  claves   Un  mapa  permite  
ÁRBITRO ÁRBITRO ÁRBITRO
que  hagan  referencia  al  mismo  objeto,  pero  no  puede   valores  
tener  claves  duplicadas.  Aunque  las  claves  suelen  ser   duplicados,  pero  
nombres  de  cadena  (por  lo  que  puede  crear  listas  de   no  claves  duplicadas.
propiedades  de  nombre/valor,  por  ejemplo),  una  clave  
“Clave  A” “Tecla  B” “Clave  C”
puede  ser  cualquier  objeto.

Mapa

Las  listas  simples,  los  conjuntos  y  los  mapas  son  inmutables,  lo  que  significa  que  no  
puede  agregar  ni  eliminar  elementos  después  de  que  se  haya  inicializado  la  colección.

Si  desea  poder  agregar  o  eliminar  elementos,  Kotlin  tiene  subtipos  mutables  que  
puede  usar  en  su  lugar:  MutableList,  MutableSet  y  MutableMap.  Entonces,  si  desea  
todos  los  beneficios  de  usar  una  Lista  y  desea  poder  actualizar  su  contenido,  use  una  
MutableList.

Ahora  que  ha  visto  los  tres  tipos  principales  de  colección  que  Kotlin  tiene  para  ofrecer,  
veamos  cómo  usa  cada  uno,  comenzando  con  una  Lista.

255
estas  aqui  4
Machine Translated by Google

Liza

Fantásticas  listas...
El  código  crea  una  lista  
Usted  crea  una  Lista  de  una  manera  similar  a  cómo  crea  una  matriz:  llamando  a  una   que  contiene  valores  de  
función  llamada  listOf,  pasando  los  valores  con  los  que  desea  que  se  inicialice  la   cadena  de  "Té",  "Huevos"  y  "Leche".
Lista.  El  siguiente  código,  por  ejemplo,  crea  una  lista,  la  inicializa  con  tres  cadenas  
y  la  asigna  a  una  nueva  variable  denominada  compras:

ÁRBITRO "Té"
val  compras  =  listOf("Té",  "Huevos",  "Leche") 0
El  compilador  infiere  el  tipo  de  objeto  que  debe  contener  cada  Lista  al   Cadena
ÁRBITRO
observar  el  tipo  de  cada  valor  que  se  le  pasa  cuando  se  crea.  La  lista   ÁRBITRO
"Huevos"
anterior,  por  ejemplo,  se  inicializa  con  tres  cadenas,  por  lo  que  el  compilador   1
crea  una  lista  de  tipo  List<String>.  También  puede  definir  explícitamente  el   compras
tipo  de  Lista  usando  un  código  como  este: Cadena
val  Lista<Cadena>
"Leche"
ÁRBITRO

2
val  compras:  Lista<String>
La  variable
compras  =  listOf("Té",  "Huevos",  "Leche") Cadena
tiene  un  tipo  
de  List<String>,  
...y  cómo  usarlos por  lo  que  List  
contiene  referencias  
Una  vez  que  haya  creado  una  lista,  puede  acceder  a  los  elementos  que  
a  objetos  String.
contiene  utilizando  la  función  de  obtención .  El  siguiente  código,  por  
ejemplo,  verifica  que  el  tamaño  de  la  Lista  sea  mayor  que  0,  luego  imprime  
el  elemento  en  el  índice  0:
Es  una  buena  idea  verificar  primero  
if  (compras.tamaño  >  0)  { el  tamaño  de  la  Lista  porque  get()  

println(compras.obtener(0))
generará  una  
ArrayIndexOutOfBoundsException  si  pasa  un  índice  no  válido.
//  Imprime  "Té"

Puede  recorrer  todos  los  elementos  en  una  Lista  de  esta  manera:

para  (artículo  en  compras)  println  (artículo)
Las  listas  y  otras  
Y  también  puede  verificar  si  la  Lista  contiene  una  referencia  a  un  objeto  en  particular  y  
colecciones  
recuperar  su  índice:
pueden  contener  
if  (compras.contiene("Leche"))  {

println(compras.indexOf("Leche")) referencias  a  cualquier  
//Imprime  2

}
tipo  de  objeto:  cadenas,  
enteros,  patos,  pizzas,  etc.
Como  puede  ver,  usar  una  lista  es  muy  parecido  a  usar  una  matriz.  Sin  embargo,  
la  gran  diferencia  es  que  una  Lista  es  inmutable:  no  puede  actualizar  ninguna  de  las  
referencias  que  almacena.

256  Capítulo  9
Machine Translated by Google

colecciones

Crear  una  Lista  Mutable...
Si  desea  una  Lista  cuyos  valores  pueda  actualizar,  debe  usar  una  
MutableList.  Usted  define  una  MutableList  de  manera  similar  a  como  
define  una  Lista,  excepto  que  esta  vez,  usa  la  función  mutableListOf  en  
su  lugar:
ÁRBITRO "Té"
val  mShopping  =  mutableListOf("Té",  "Huevos")
0
ÁRBITRO
MutableList  es  un  subtipo  de  Lista,  por  lo  que  puede  llamar   Cadena
a  las  mismas  funciones  en  una  MutableList  que  en  una  Lista.  
Sin  embargo,  la  gran  diferencia  es  que  MutableLists  tiene   mCompras ÁRBITRO
"Huevos"
1
funciones  adicionales  que  puede  usar  para  agregar  o  
eliminar  valores,  o  actualizar  o  reorganizar  los  existentes. valor
Cadena
ListaMutable<String>

..y  agregarle  valores Si  pasa  valores  de  cadena  a  la  
función  mutableListOf(),  el  compilador  
Agrega  nuevos  valores  a  una  MutableList  usando  la  función  de  agregar . infiere  que  desea  un  objeto  de  tipo  
Si  desea  agregar  un  nuevo  valor  al  final  de  MutableList,  pase  el  valor  a  la   MutableList<String>  (una  MutableList  que  
función  de  agregar  como  un  solo  parámetro. contiene  cadenas).
El  siguiente  código,  por  ejemplo,  agrega  "Milk"  al  final  de  mShopping:

mShopping.add("Leche")

Esto  aumenta  el  tamaño  de  MutableList  para  que  ahora  tenga  tres  valores  en  
lugar  de  dos.

Si  desea  insertar  un  valor  en  un  índice  específico,  puede  hacerlo  
pasando  el  valor  del  índice  a  la  función  de  suma  además  del  valor.  Si  
ÁRBITRO "Té"
quisiera  insertar  un  valor  de  "Milk"  en  el  índice  1  en  lugar  de  agregarlo  
0
al  final  de  MutableList,  podría  hacerlo  usando  el  siguiente  código:
Cadena
ÁRBITRO

mShopping.add(1,  "Leche") ÁRBITRO
"Leche"
1
Insertar  un  valor  en  un  índice  específico  de  esta   mCompras
manera  obliga  a  otros  valores  a  moverse  para  hacerle   Cadena
espacio.  En  este  ejemplo,  el  valor  "Huevos"  se  mueve   valor ÁRBITRO

del  índice  1  al  índice  2  para  que  "Leche"  se  pueda   ListaMutable<String>
"Huevos"
insertar  en  el  índice  1.
2

Si  agrega  "Leche"  al   Cadena
Además  de  agregar  valores  a  MutableList,  también  
puede  eliminarlos  o  reemplazarlos.  Veamos  cómo. índice  1,  "Huevos"  se  
mueve  al  índice  2  para  
dar  paso  al  nuevo  valor.

estas  aqui  4
257
Machine Translated by Google

modificando  una  MutableList

Puede  eliminar  un  valor...
Hay  dos  formas  de  eliminar  un  valor  de  MutableList.

La  primera  forma  es  llamar  a  la  función  de  eliminación ,   ÁRBITRO "Té"


0
ÁRBITRO
pasando  el  valor  que  desea  eliminar.  El  siguiente  código,  por  
ejemplo,  verifica  si  mShopping  contiene  la  cadena  "Milk"  y  
Cadena
luego  la  elimina: mCompras
ÁRBITRO
"Leche"
if  (mShopping.contains("Leche"))  { 1
valor
mShopping.remove("Leche") ListaMutable<String>
Cadena
}
ÁRBITRO
Eliminando   "Huevos"
La  segunda  forma  es  usar  la  función  removeAt  para  eliminar  el   un  elemento  de   2
valor  en  un  índice  dado.  El  siguiente  código,  por  ejemplo,  se  
MutableList...
asegura  de  que  el  tamaño  de  mShopping  sea  mayor  que  1  y   Cadena
luego  elimina  el  valor  en  el  índice  1: ...hace  que  
MutableList  
if  (mCompras.tamaño  >  1)  { se  reduzca.
mShopping.removeAt(1) ÁRBITRO "Té"

} 0
ÁRBITRO

Cadena
Cualquiera  que  sea  el  enfoque  que  utilice,  eliminar  un  valor  
de  MutableList  hace  que  se  reduzca.
mCompras ÁRBITRO
"Huevos"
1

...y  reemplazar  un  valor  por  otro valor
Cadena
ListaMutable<String>
Si  desea  actualizar  MutableList  para  que  el  valor  en  un  
índice  particular  se  reemplace  con  otro,  puede  hacerlo   Como  se  eliminó  
usando  la  función  set .  El  siguiente  código,  por  ejemplo,   "Leche",  "Huevos"  
reemplaza  el  valor  "Té"  en  el  índice  0  con  "Café":
pasa  del  índice  2  al  índice  1.

if  (mCompras.tamaño  >  0)  { "Té"
mCompras.set(0,  "Café")
}
Cadena
La  función  set()  establece  
la  referencia  contenida  
ÁRBITRO "Café"
en  un  índice  particular  a  
0
ÁRBITRO la  de  un  objeto  diferente.
Cadena

mCompras ÁRBITRO
"Huevos"
1

valor
Cadena
ListaMutable<String>

258  Capítulo  9
Machine Translated by Google

colecciones

Puedes  cambiar  el  orden  y  hacer  cambios  masivos...
MutableList  también  incluye  funciones  para  cambiar  el  orden  en  que  se  guardan  los  
elementos.  Puede,  digamos,  ordenar  MutableList  en  un  orden  natural  usando  la  
función  de  ordenación ,  o  revertirlo  usando  reversa:

mShopping.sort() Juntas,  estas  líneas  ordenan  
mCompras.reverse() MutableList  en  orden  inverso.

P:  ¿ Qué  es  un  paquete?
O  puede  usar  la  función  de  reproducción  aleatoria  para  aleatorizarlo:

mCompras.shuffle() R:  Un  paquete  es  una  agrupación  de  clases.
y  funciones  Son  útiles  por  un  par  de  
Y  también  hay  funciones  útiles  para  realizar  cambios  masivos  en  MutableList.   razones.
Puede,  por  ejemplo,  usar  la  función  addAll  para  agregar  todos  los  elementos  que  se  
encuentran  en  otra  colección.  El  siguiente  código,  por  ejemplo,  agrega  "Cookies"  y   Primero,  ayudan  a  organizar  un  proyecto  o  

"Sugar"  a  mShopping: una  biblioteca.  En  lugar  de  tener  solo  una  
gran  cantidad  de  clases,  todas  están  agrupadas  
val  toAdd  =  listOf("Galletas",  "Azúcar") en  paquetes  para  tipos  específicos  de  
mShopping.addAll(toAdd) funcionalidad.

La  función  removeAll  elimina  elementos  que  se  encuentran  en  otra  colección:
En  segundo  lugar,  le  brindan  un  alcance  
de  nombre,  lo  que  significa  que  varias  
personas  pueden  escribir  clases  con  el  mismo  
val  toRemove  =  listOf("Leche",  "Azúcar")
nombre,  siempre  que  estén  en  diferentes  paquetes.
mShopping.removeAll(toRemove)
Encontrará  más  información  sobre  cómo  estructurar  
Y  la  función  preserveAll  conserva  todos  los  elementos  que  se  encuentran  en  otra  
su  código  en  paquetes  en  el  Apéndice  III.
colección  y  elimina  todo  lo  demás:

val  toRetain  =  listOf("Leche",  "Azúcar") P:  En  Java  tengo  que  importar  cualquier
paquetes  que  quiero  usar,  incluidas  
mShopping.retainAll(toRetain)
las  colecciones.  ¿Yo  en  Kotlin?

También  puede  usar  la  función  borrar  para  eliminar  todos  los  elementos  como  este:
R:  Kotlin  importa  automáticamente  muchos
mCompras.clear() Esto  vacía  mShopping  por  lo  que  su  tamaño  es  0.
paquetes  de  la  biblioteca  estándar  de  
Kotlin,  incluido  kotlin.collections.  Sin  embargo,  

...o  tomar  una  copia  de  MutableList  completa todavía  hay  situaciones  en  las  que  necesita  
importar  paquetes  explícitamente  y  puede  
A  veces,  puede  ser  útil  copiar  una  Lista,  o  MutableList,  para  que  pueda  guardar  una   obtener  más  información  en  el  Apéndice  III.
instantánea  de  su  estado.  Puede  hacer  esto  usando  la  función  toList .  El  siguiente  
código,  por  ejemplo,  copia  mShopping  y  asigna  la  copia  a  una  nueva  variable  
denominada  shoppingSnapshot:

val  shoppingCopy  =  mShopping.toList()

La  función  toList  devuelve  una  Lista,  no  una  MutableList,  por  lo  que  shoppingCopy   MutableList  también  tiene  una  función  

no  se  puede  actualizar.  Otras  funciones  útiles  que  puede  usar  para  copiar  MutableList   toMutableList()  que  devuelve  una  copia  que  es  
incluyen  sorted  (que  devuelve  una  Lista  ordenada),  invertida  (que  devuelve  una  Lista   una  MutableList  nueva.

con  los  valores  en  orden  inverso)  y  barajada  (que  devuelve  una  Lista  y  mezcla  sus  
valores).

estas  aqui  4 259
Machine Translated by Google

crear  proyecto

Crear  el  proyecto  Colecciones
Ahora  que  aprendió  sobre  Listas  y  MutableLists,  creemos  un  proyecto  que  las  use.

Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asígnele  el  nombre  
"Colecciones".  Luego  cree  un  nuevo  archivo  de  Kotlin  llamado  Collections.kt  resaltando  la  
carpeta  src ,  haciendo  clic  en  el  menú  Archivo  y  eligiendo  Nuevo  →  Archivo/clase  de  Kotlin.  
Cuando  se  le  solicite,  asigne  al  archivo  el  nombre  "Colecciones"  y  elija  Archivo  en  la  opción  Tipo.

A  continuación,  agregue  el  siguiente  código  a  Collections.kt:

diversión  principal(argumentos:  Array<String>)  {
val  mListaDeCompras  =  mutableListOf("Té",  "Huevos",  "Leche")
println("mShoppingList  original:  $mShoppingList")
val  extraShopping  =  listOf("Galletas",  "Azúcar",  "Huevos")
mShoppingList.addAll(compras  adicionales)
println("artículos  mShoppingList  agregados:  $mShoppingList")
if  (mListaDeCompras.contains("Té"))  {
mShoppingList.set(mShoppingList.indexOf("Té"),  "Café")
}

mListaDeCompras.sort()
println("mListaCompras  ordenada:  $mListaCompras") Colecciones

mListaDeCompras.reverse()
println("mListaDeCompras  invertida:  $mListaDeCompras") origen

Colecciones.kt

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  salida  del  
IDE:
La  impresión  de  una  
mShoppingList  original:  [Té,  Huevos,  Leche]   Lista  o  MutableList  imprime  
mShoppingList  elementos  agregados:  [Té,  Huevos,  Leche,  Galletas,  Azúcar,   cada  elemento  en  orden  de  

Huevos]  mShoppingList  ordenada:  [Café,  Galletas,  Huevos,  Huevos,  Leche,  Azúcar]   índice  dentro  de  corchetes.

mShoppingList  invertida:  [Azúcar,  Leche ,  Huevos,  Huevos,  Galletas,  Café]

A  continuación,  inténtalo  con  el  siguiente  ejercicio.

260  Capítulo  9
Machine Translated by Google

colecciones

La  función  necesita  producir  esta  salida.
Imanes  de  código
Alguien  usó  imanes  de  nevera  para  crear  una   [Cero,  Dos,  Cuatro,  Seis]
función  principal  funcional  que  produzca  el   [Dos,  Cuatro,  Seis,  Ocho]
resultado  que  se  muestra  a  la  derecha.  
[Dos,  Cuatro,  Seis,  Ocho,  Diez]
Desafortunadamente,  un  extraño  sharknado  ha  desalojado  los  imanes.
Vea  si  puede  reconstruir  la  función. [Dos,  Cuatro,  Seis,  Ocho,  Diez]

Su  código  debe  ir  aquí.

a.add(2,  "Cuatro") a.add(0,  "Cero")  
var  a:  MutableList<String>  =  mutableListOf()
a.add(1,  "Dos")
imprimir(a)
diversión  principal(argumentos:  Array<String>)  {
imprimir(a) if  (a.indexOf("Cuatro") !=  4)  a.add("Diez")
a.add(3,  "Seis")
imprimir(a) if  (a.contains("Cero"))  a.add("Ocho")
imprimir(a)

} a.removeAt(0) if  (a.contains("Zero"))  a.add("Twelve")

estas  aqui  4 261
Machine Translated by Google

solución  de  imanes

Solución  de  imanes  de  código
Alguien  usó  imanes  de  nevera  para  crear  una   [Cero,  Dos,  Cuatro,  Seis]
función  principal  funcional  que  produzca  el   [Dos,  Cuatro,  Seis,  Ocho]
resultado  que  se  muestra  a  la  derecha.  
[Dos,  Cuatro,  Seis,  Ocho,  Diez]
Desafortunadamente,  un  extraño  sharknado  ha  desalojado  los  imanes.
Vea  si  puede  reconstruir  la  función. [Dos,  Cuatro,  Seis,  Ocho,  Diez]

diversión  principal(argumentos:  Array<String>)  {

var  a:  MutableList<String>  =  mutableListOf()

a.add(0,  "Cero")  
a.add(1,  "Dos")

a.add(2,  "Cuatro")

a.add(3,  "Seis")

imprimir(a)

if  (a.contains("Cero"))  a.add("Ocho")

a.removeAt(0)

imprimir(a)

if  (a.indexOf("Cuatro") !=  4)  a.add("Diez")

imprimir(a)

if  (a.contains("Zero"))  a.add("Twelve")

imprimir(a)

262  Capítulo  9
Machine Translated by Google

colecciones

Las  listas  permiten  valores  duplicados

Como  ya  aprendió,  el  uso  de  una  Lista  o  MutableList  le  brinda  más  
flexibilidad  que  el  uso  de  una  matriz.  A  diferencia  de  una  matriz,  puede  
elegir  explícitamente  si  la  colección  debe  ser  inmutable  o  si  su  código  
puede  agregar,  eliminar  y  actualizar  sus  valores.

Sin  embargo,  hay  algunas  situaciones  en  las  que  el  uso  de  una  Lista  
(o  MutableList)  no  funciona  del  todo.

Imagina  que  estás  organizando  una  comida  con  un  grupo  de  amigos  y  
necesitas  saber  cuántas  personas  van  a  ir  para  poder  reservar  una  
mesa.  Podría  usar  una  Lista  para  esto,  pero  hay  un  problema:  una  Lista  
puede  contener  valores  duplicados.  Es  posible,  por  ejemplo,  crear  una  
Lista  de  amigos  donde  algunos  de  los  amigos  se  enumeran  dos  veces:
ÁRBITRO
"Jim"

0
val  listaAmigos  =  listaDe("Jim", Cadena
Aquí  hay  tres  amigos   "Demandar",
ÁRBITRO
ÁRBITRO
llamados  Jim,  Sue  y  Nick,   "Demandar",
1
pero  Sue  y  Nick  aparecen   "Mella", amigo "Demandar"
dos  veces  en  la  lista. Lista
"Mella")
ÁRBITRO

val  Lista<Cadena> Cadena
Pero  si  quiere  saber  cuántos  amigos  distintos  hay  en  la  Lista,   2
no  puede  simplemente  usar  el  código:

listaamigos.tamaño ÁRBITRO

3 "Mella"
para  saber  para  cuántas  personas  debes  reservar  una  mesa.  La  
propiedad  de  tamaño  solo  ve  que  hay  cinco  elementos  en  la  Lista  y  no  
le  importa  que  dos  de  estos  elementos  estén  duplicados. ÁRBITRO Cadena

En  este  tipo  de  situación,  necesitamos  usar  una  colección  que  no   4
permita  que  se  mantengan  valores  duplicados.  Entonces,  ¿qué  tipo   La  Lista  tiene  un  
de  colección  debemos  usar? tamaño  de  5,  pero  
solo  3  valores  distintos.

Anteriormente  en  el  capítulo,  discutimos  los  diferentes  tipos  de  colección  
que  están  disponibles  en  Kotlin.  ¿Qué  tipo  de  colección  crees  que  sería  la  más  
apropiada  para  esta  situación?

estas  aqui  4 263
Machine Translated by Google

Conjuntos

Cómo  crear  un  conjunto
Si  necesita  una  colección  que  no  permita  duplicados,  puede  usar  un  Conjunto:  una  
colección  desordenada  sin  valores  duplicados.

Usted  crea  un  Conjunto  llamando  a  una  función  llamada  setOf,  pasando  los  
valores  con  los  que  desea  que  se  inicialice  el  Conjunto.  El  siguiente  código,  por   El  código  crea  un  
ejemplo,  crea  un  conjunto,  lo  inicializa  con  tres  cadenas  y  lo  asigna  a  una  nueva  
Conjunto  que  contiene  
variable  llamada  friendSet:
los  tres  valores  de  Cadena.

val  friendSet  =  setOf("Jim",  "Sue",  "Nick")
"Jim"

Un  conjunto  no  puede  contener  valores  duplicados,  por  lo  que  si  intenta  definir  uno  
usando  un  código  como  este:
ÁRBITRO
Cadena
ÁRBITRO ÁRBITRO
val  friendSet  =  setOf("Jim", "Demandar"

"Demandar", amigo
Colocar
"Demandar",
ÁRBITRO
Cadena
"Mella", val  Set<Cadena>
"Mella") "Mella"

el  conjunto  ignora  los  valores  duplicados  de  "Sue"  y  "Nick".  El  código  crea  un  
Los  valores  de  un  conjunto   Cadena
Conjunto  que  contiene  tres  Cadenas  distintas  como  antes.
no  tienen  orden  y  no  se  
El  compilador  infiere  el  tipo  del  Conjunto  al  observar  los  valores  que  se  le  pasan   permiten  valores  duplicados.
cuando  se  crea.  El  código  anterior,  por  ejemplo,  inicializa  un  conjunto  con  valores  
de  cadena,  por  lo  que  el  compilador  crea  un  conjunto  de  tipo  Set<String>.

Cómo  usar  los  valores  de  un  conjunto

Los  valores  de  un  conjunto  no  están  ordenados,  por  lo  que,  a  diferencia  de  una  
lista,  no  hay  una  función  de  obtención  que  pueda  usar  para  obtener  el  valor  en  un  
índice  específico.  Sin  embargo,  aún  puede  usar  la  función  contiene  para  verificar  si  
un  conjunto  contiene  un  valor  particular  usando  un  código  como  este: Esto  devuelve  verdadero  si  friendSet  tiene  
un  valor  "Fred",  y  falso  si  no  lo  tiene.
val  isFredGoing  =  friendSet.contains("Fred")

Y  también  puede  recorrer  un  conjunto  como  este:

for  (elemento  en  friendSet)  println(elemento)

Un  conjunto  es  inmutable,  por  lo  que  no  puede  agregarle  valores  ni  eliminar   A  diferencia  de  una  lista,  un  
los  existentes.  Para  hacer  este  tipo  de  cosas,  necesitaría  usar  un  MutableSet  
en  su  lugar.  Pero  antes  de  que  le  mostremos  cómo  crear  y  usar  uno  de  estos,   conjunto  no  está  ordenado  y  
hay  una  pregunta  importante  que  debemos  analizar:  ¿ cómo  decide  un  conjunto  
si  un  valor  es  un
no  puede  contener  valores  duplicados.
¿duplicar?

264  Capítulo  9
Machine Translated by Google

colecciones

Cómo  un  conjunto  comprueba  si  hay  duplicados

Para  responder  a  esta  pregunta,  repasemos  los  pasos  que  sigue  
un  Conjunto  cuando  decide  si  un  valor  es  un  duplicado  o  no.

1
El  Conjunto  obtiene  el  código  hash  del  objeto  y  lo  compara  
con  los  códigos  hash  de  los  objetos  que  ya  están  en  el   Necesito  saber  si  
Conjunto. los  valores  de  su  
Un  conjunto  utiliza  códigos  hash  para  almacenar  sus  elementos  de  una   código  hash  son  los  mismos.
manera  que  hace  que  sea  mucho  más  rápido  acceder  a  ellos.  Utiliza  el  
código  hash  como  una  especie  de  etiqueta  en  un  "cubo"  donde  almacena  

elementos,  por  lo  que  todos  los  objetos  con  un  código  hash  de,  por  
código  hash:  742
ejemplo,  742,  se  almacenan  en  el  cubo  etiquetado  como  742.

ÁRBITRO
Si  el  Conjunto  no  tiene  códigos  hash  coincidentes  para  el  
nuevo  valor,  el  Conjunto  asume  que  no  es  un  duplicado  y  
agrega  el  nuevo  valor.  Sin  embargo,  si  el  conjunto  tiene  
código  hash:  742
códigos  hash  coincidentes,  debe  realizar  pruebas  adicionales  
y  pasar  al  paso  2. Colocar

2 El  Conjunto  utiliza  el  operador  ===  para  comparar  
Tus  códigos  hash

el  nuevo  valor  con  cualquier  objeto  que  contenga   son  lo  mismo.  ¿Eres  el  
con  el  mismo  código  hash. mismo  objeto...?

Como  aprendió  en  el  Capítulo  7,  el  operador  ===
código  hash:  742
se  utiliza  para  comprobar  si  dos  referencias  se  refieren  a
el  mismo  objeto  Entonces,  si  el  operador  ===  devuelve   ÁRBITRO

verdadero  para  cualquier  objeto  con  el  mismo  código  
hash,  el  Conjunto  sabe  que  el  nuevo  valor  es  un  
duplicado,  por  lo  que  lo  rechaza.  Sin  embargo,  si  el   código  hash:  742

operador  ===  devuelve  falso,  el  Conjunto  pasa  al  paso  3. Colocar

3 El  Conjunto  utiliza  el  operador  ==  para  comparar  el  nuevo  
...  ¿O  sois  iguales?
valor  con  cualquier  objeto  que  contenga  con  códigos  hash  
coincidentes.
El  operador  ==  llama  a  la  función  de  igualdad  del  valor.
código  hash:  742
Si  esto  devuelve  verdadero,  el  Conjunto  trata  el  nuevo  valor  
como  un  duplicado  y  lo  rechaza.  Sin  embargo,  si  el  operador   ÁRBITRO

==  devuelve  falso,  el  Conjunto  asume  que  el  nuevo  valor  no  
es  un  duplicado  y  lo  agrega.
código  hash:  742
Entonces,  hay  dos  situaciones  en  las  que  un  Conjunto  ve  un  
Colocar
valor  como  un  duplicado:  si  es  el  mismo  objeto  o  si  es  igual  a  un  
valor  que  ya  contiene.  Veamos  esto  con  más  detalle.

estas  aqui  4 265
Machine Translated by Google

la  igualdad  importa

Códigos  hash  e  igualdad
Como  aprendió  en  el  Capítulo  7,  el  operador  ===  verifica  si  dos  referencias  apuntan  
al  mismo  objeto,  y  el  operador  ==  verifica  si  las  referencias  apuntan  a  objetos  que  deben  
considerarse  iguales.  Sin  embargo,  un  conjunto  solo  usa  estos  operadores  una  vez  que  se  
establece  que  los  dos  objetos  tienen  valores  de  código  hash  coincidentes.  Esto  significa  
que  para  que  un  conjunto  funcione  correctamente,  los  objetos  iguales  deben  tener  códigos  
hash  coincidentes.

Veamos  cómo  se  aplica  esto  a  los  operadores  ===  y  ==.

Igualdad  usando  el  operador  ===
ÁRBITRO

Si  tiene  dos  referencias  que  hacen  referencia  al  mismo  objeto,  obtendrá  el   Código  
hash  "Sue":  83491
mismo  resultado  cuando  llame  a  la  función  hashCode  en  cada  referencia.  Si   a
no  anula  la  función  hashCode,  el  comportamiento  predeterminado  (que  hereda  
de  la  superclase  Any)  es  que  cada  objeto  obtendrá  un  código  hash  único.
valor  cadena Cadena

ÁRBITRO

Cuando  se  ejecuta  el  siguiente  código,  el  Conjunto  detecta  que  a  y  b  
Aquí,  a  y  b  se  refieren  

tienen  el  mismo  código  hash  y  se  refieren  al  mismo  objeto,  por  lo  que  se   al  mismo  objeto,  por  lo  
b
agrega  un  valor  al  Conjunto:
que  el  Conjunto  sabe  que  
b  es  un  duplicado  de  a.
val  a  =  "Sue" valor  cadena

valor  b  =  un

val  set  =  setOf(a,  b)

Igualdad  usando  el  operador  ==
Código  hash  
Si  desea  que  un  Conjunto  trate  dos  objetos  Receta  diferentes  como  iguales   “Curry  tailandés”:  64
o  equivalentes,  tiene  dos  opciones:  convertir  Receta  en  una  clase  de  datos  
o  anular  las  funciones  hashCode  y  equals  que  hereda  de  Cualquiera.  Convertir   ÁRBITRO

a  Recipe  en  una  clase  de  datos  es  más  fácil,  ya  que  anula  automáticamente   Receta
las  dos  funciones. a

Como  dijimos  anteriormente,  el  comportamiento  predeterminado  (de  
Cualquiera)  es  dar  a  cada  objeto  un  valor  de  código  hash  único.  Por  lo   val  Receta Código  hash  
tanto,  debe  anular  hashCode  para  asegurarse  de  que  dos  objetos   “Curry  tailandés”:  64
equivalentes  devuelvan  el  mismo  código  hash.  Pero  también  debe  anular  
los  valores  iguales  para  que  el  operador  ==  devuelva  verdadero  cuando  se   ÁRBITRO

usa  para  comparar  objetos  con  valores  de  propiedad  coincidentes.
Receta
b
En  el  siguiente  ejemplo,  se  agregará  un  valor  al  conjunto  si  la  receta  es  una   Aquí,  a  y  b  se  refieren  a  
clase  de  datos: objetos  separados.  El  
val  Receta
Conjunto  ve  a  b  como  un  
val  a  =  Receta  ("Curry  tailandés") duplicado  solo  si  a  y  b  tienen  
val  b  =  Receta  ("Curry  tailandés") el  mismo  valor  de  código  hash,  
val  set  =  setOf(a,  b) y  a  ==  b.  Este  será  el  caso  si  
Receta  es  una  clase  de  datos.

266  Capítulo  9
Machine Translated by Google

colecciones

Reglas  para  anular  
hashCode  y  equals
Si  decide  anular  manualmente  las  funciones  hashCode  y  equals  
en  su  clase  en  lugar  de  usar  una  clase  de  datos,  hay  una  serie  de  leyes   P:  ¿ Cómo  pueden  los  códigos  hash  ser  iguales  incluso  si
que  debe  cumplir.  Si  no  lo  hace,  el  universo  de  Kotlin  colapsará  porque   los  objetos  no  son  iguales?
cosas  como  los  Conjuntos  no  funcionarán  correctamente,  así  que  
asegúrese  de  seguirlos.
R:  Como  dijimos  antes,  un  Conjunto  usa  códigos  hash
Estas  son  las  reglas: para  almacenar  sus  elementos  de  una  manera  que  lo  
haga  mucho  más  rápido  de  acceder.  Si  desea  encontrar  

¥ Si  dos  objetos  son  iguales,  deben  tener  códigos   un  objeto  en  un  conjunto,  no  tiene  que  comenzar  a  

hash  coincidentes. buscar  desde  el  principio,  mirando  cada  elemento  para  
ver  si  coincide.  En  cambio,  usa  el  código  hash  como  una  
¥ Si  dos  objetos  son  iguales,  llamar  a  equals  en   etiqueta  en  un  "cubo"  donde  almacenó  el  elemento.  
cualquiera  de  los  objetos  debe  devolver  verdadero.  En   Entonces,  si  dices  "Quiero  encontrar  un  objeto  en  el  
otras  palabras,  si  (a.  es  igual  a  (b))  entonces  (b.  es  igual  a  (a)). Conjunto  que  se  parezca  a  este...",  el  Conjunto  obtiene  el  
valor  del  código  hash  del  objeto  que  le  das,  luego  va  
¥ Si  dos  objetos  tienen  el  mismo  valor  de  código  hash,  
directamente  al  cubo  para  ese  código  hash.
no  es  necesario  que  sean  iguales.  Pero  si  son  iguales,  
deben  tener  el  mismo  valor  de  código  hash.
Esta  no  es  toda  la  historia,  pero  es  más  que  suficiente  para  
usar  un  conjunto  de  manera  efectiva  y  comprender  lo  que  
¥ Entonces,  si  anula  equals,  debe  anular  hashCode. está  sucediendo.

El  punto  es  que  los  códigos  hash  pueden  ser  
¥ El  comportamiento  predeterminado  de  la  función  
iguales  sin  garantizar  necesariamente  que  los  objetos  
hashCode  es  generar  un  número  entero  único  
sean  iguales,  porque  el  "algoritmo  hash"  utilizado  en  la  
para  cada  objeto.  Entonces,  si  no  anula  
función  hashCode  podría  devolver  el  mismo  valor  para  
hashCode  en  una  clase  que  no  es  de  datos,  no  hay   varios  objetos.  Y  sí,  eso  significa  que  todos  los  objetos  
dos  objetos  de  ese  tipo  que  puedan  considerarse  iguales. múltiples  aterrizarían  en  el  mismo  cubo  en  el  Conjunto  

¥ El  comportamiento  predeterminado  de  la  función  equals   (porque  cada  cubo  representa  un  valor  de  código  hash  

es  hacer  una  comparación  ===,  que  prueba  si  las  dos   separado),  pero  ese  no  es  el  fin  del  mundo.  Puede  
significar  que  el  Conjunto  es  un  poco  menos  eficiente,  
referencias  se  refieren  a  un  solo  objeto.  Por  lo  tanto,  si  
o  que  está  lleno  de  una  cantidad  extremadamente  
no  anula  los  valores  iguales  en  una  clase  que  no  es  de  
grande  de  elementos,  pero  si  el  Conjunto  encuentra  
datos,  dos  objetos  nunca  se  pueden  considerar  iguales,  
más  de  un  objeto  en  el  mismo  cubo  de  código  hash,  el  
ya  que  las  referencias  a  dos  objetos  diferentes  siempre  
Conjunto  simplemente  usará  el  ===  y  ==  operadores  
contendrán  un  patrón  de  bits  diferente.
para  buscar  una  combinación  perfecta.  En  otras  
palabras,  los  valores  del  código  hash  a  veces  se  usan  
a.equals(b)  también  debe  significar   para  restringir  la  búsqueda,  pero  para  encontrar  la  
coincidencia  exacta,  el  Conjunto  todavía  tiene  que  
que  a.hashCode()  ==  b.hashCode() tomar  todos  los  objetos  en  ese  cubo  (el  cubo  para  todos  
los  objetos  con  el  mismo  código  hash)  y  vea  si  hay  un  
objeto  coincidente  en  ese  depósito.

Pero  a.hashCode()  ==  b.hashCode()  no  

tiene  por  qué  significar  que  a.equals(b)
estas  aqui  4 267
Machine Translated by Google

MutableSet

Cómo  usar  un  MutableSet
Ahora  que  sabe  acerca  de  los  Conjuntos,  echemos  un  vistazo  a  
MutableSets.  Un  MutableSet  es  un  subtipo  de  Set,  pero  con  funciones  
adicionales  que  puede  usar  para  agregar  y  eliminar  valores.

Defina  un  MutableSet  usando  la  función  mutableSetOf  de  esta  manera: "Jim"

val  mFriendSet  =  mutableSetOf("Jim",  "Sue") ÁRBITRO
Cadena
ÁRBITRO
ÁRBITRO
Esto  inicializa  un  MutableSet  con  dos  cadenas,  por  lo  que  el   "Demandar"
amigo  m
compilador  infiere  que  desea  un  MutableSet  de  tipo  
Colocar
MutableSet<String>.
Cadena
valor
Agrega  nuevos  valores  a  un  MutableSet  usando  la  función  de  
agregar .  El  siguiente  código,  por  ejemplo,  agrega  "Nick"  a   MutableSet<String>
mFriendSet:
Si  pasa  valores  de  cadena  a  la  
mFriendSet.add("Nick") función  mutableSetOf(),  el  compilador  
infiere  que  desea  un  objeto  de  tipo  
La  función  de  agregar  verifica  si  el  objeto  que  se  pasa  ya  existe  en   MutableSet<String>  (un  MutableSet  
MutableSet.  Si  encuentra  un  valor  duplicado,  devuelve  falso.  Sin  embargo,   que  contiene  cadenas).
si  no  es  un  duplicado,  el  valor  se  agrega  al  MutableSet  (aumentando  su  
tamaño  en  uno)  y  la  función  devuelve  verdadero  para  indicar  que  la  operación  
fue  exitosa.

Elimina  valores  de  un  MutableSet  usando  la  función  de  eliminación.  El  
siguiente  código,  por  ejemplo,  elimina  "Nick"  de  mFriendSet:

mFriendSet.remove("Nick")

Si  "Nick"  existe  en  MutableSet,  la  función  lo  elimina  y  devuelve  
"Jim"
verdadero.  Sin  embargo,  si  no  hay  ningún  objeto  coincidente,  la  función  
simplemente  devuelve  falso.
ÁRBITRO
Cadena
También  puede  usar  las  funciones  addAll,  removeAll  y  retainAll  para  
ÁRBITRO
realizar  cambios  masivos  en  MutableSet,  tal  como  puede  hacerlo  con   "Demandar"
MutableList.  La  función  addAll,  por  ejemplo,  agrega  todos  los  elementos  al  
MutableSet  que  se  encuentran  en  otra  colección,  por  lo  que  puede  usar  el  
siguiente  código  para  agregar  "Joe"  y  "Mia"  a  mFriendSet: ÁRBITRO
Cadena

"José"
addAll()  agrega  
val  toAdd  =  setOf("Joe",  "Mia")
los  valores   ÁRBITRO

mFriendSet.addAll(paraAgregar) contenidos  en  otro  Conjunto. Cadena

Y  tal  como  puede  hacerlo  con  MutableList,  puede  usar  la  función   "Desaparecido  en  combate"

borrar  para  eliminar  todos  los  elementos  del  MutableSet:

mFriendSet.clear()
Cadena

268  Capítulo  9
Machine Translated by Google

colecciones

Puedes  copiar  un  MutableSet
Si  desea  tomar  una  instantánea  de  un  MutableSet,  puede  
hacerlo,  al  igual  que  con  una  MutableList.  Puede  usar  la  función  
toSet ,  por  ejemplo,  para  tomar  una  copia  inmutable  de  
mFriendSet  y  asignar  la  copia  a  una  nueva  variable  llamada  
friendSetCopy:

val  friendSetCopy  =  mFriendSet.toSet()

También  puede  copiar  un  Set  o  MutableSet  en  un  nuevo  objeto  
List  usando  toList:

val  lista  de  amigos  =  mFriendSet.toList()
MutableSet  también  tiene  una  función  

Y  si  tiene  una  MutableList  o  List,  puede  copiarla  en  un  Set  usando   toMutableSet()  (que  lo  copia  en  un  nuevo  
MutableSet)  y  toMutableList()  (que  lo  copia  en  
su  función  toSet :
un  nuevo  MutableList).
val  shoppingSet  =  mShopping.toSet()

Copiar  una  colección  en  otro  tipo  puede  ser  particularmente  útil  
cuando  desea  realizar  alguna  acción  que  de  otro  modo  sería  
ineficaz.  Puede,  por  ejemplo,  comprobar  si  una  Lista  contiene  
valores  duplicados  copiando  la  Lista  en  un  Conjunto  y  comprobando  
el  tamaño  de  cada  colección.  El  siguiente  código  usa  esta  técnica  
para  verificar  si  mShopping  (una  MutableList)  contiene  duplicados: Esto  crea  una  versión  Set  de  

mShopping  y  obtiene  su  tamaño.

if  (mCompras.tamaño  >  mCompras.toSet().tamaño)  {
//mShopping  tiene  valores  duplicados
}

Si  mShopping  contiene  duplicados,  su  tamaño  será  mayor  
que  cuando  se  copia  en  un  Conjunto,  porque  al  convertir  
MutableList  en  un  Conjunto  se  eliminarán  los  valores  duplicados.

"Té"
ÁRBITRO
"Té" Cuando  la  
Esta  es  una  Lista. 0 lista  se  copia  en  
Tiene  tres   ÁRBITRO Cadena
Cadena un  conjunto,  se  
elementos,  por  
ÁRBITRO
elimina  el  valor   ÁRBITRO

lo  que  su  tamaño  es  3. "Café"
1 duplicado  de  
"Café"
"Café".
ÁRBITRO El  tamaño  del   Cadena
2 Cadena conjunto  es  de  2.

estas  aqui  4 269
Machine Translated by Google

proyecto  de  actualización

Actualizar  el  proyecto  Colecciones
Ahora  que  conoce  Sets  y  MutableSets,  actualicemos  el  proyecto  Collections  para  
que  los  use.

Actualice  su  versión  de  Collections.kt  para  que  coincida  con  la  nuestra  a  continuación  

(nuestros  cambios  están  en  negrita):

diversión  principal(argumentos:  Array<String>)  {
val  var  mShoppingList  =  mutableListOf("Té",  "Huevos",  "Leche")

Actualice  mShoppingList  a   println("mShoppingList  original:  $mShoppingList")
una  var  para  que  podamos   val  extraShopping  =  listOf("Galletas",  "Azúcar",  "Huevos")
reemplazarla  con  otra   mShoppingList.addAll(compras  adicionales)
MutableList<String>  más  
println("artículos  mShoppingList  agregados:  $mShoppingList")
adelante  en  el  código.
if  (mListaDeCompras.contains("Té"))  {
mShoppingList.set(mShoppingList.indexOf("Té"),  "Café")
}

mListaDeCompras.sort()
Colecciones
println("mListaCompras  ordenada:  $mListaCompras")
mListaDeCompras.reverse()
origen

println("mListaDeCompras  invertida:  $mListaDeCompras")

Colecciones.kt
val  mShoppingSet  =  mShoppingList.toMutableSet()
println("mConjuntoCompras:  $mConjuntoCompras")

Agrega  este  código. val  másCompras  =  setOf("Cebollino",  "Espinacas",  "Leche")
mShoppingSet.addAll(moreShopping)  
println("mShoppingSet  artículos  agregados:  $mShoppingSet")
mShoppingList  =  mShoppingSet.toMutableList()
println("mShoppingList  nueva  versión:  $mShoppingList")
}

Tomemos  el  código  para  una  prueba  de  manejo.

270  Capítulo  9
Machine Translated by Google

colecciones

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  el
Ventana  de  salida  del  IDE:

mShoppingList  original:  [Té,  Huevos,  Leche]  
mShoppingList  elementos  agregados:  [Té,  Huevos,  Leche,  Galletas,  Azúcar,  
Huevos]  mShoppingList  ordenado:  [Café,  Galletas,  Huevos,  Huevos,  Leche,  Azúcar] Al  imprimir  un  conjunto  
mShoppingList  invertida:  [Azúcar,  Leche,  Huevos,  Huevos,  Galletas,  Café] o  un  conjunto  mutable,  se  
imprime  cada  elemento  entre  corchetes.
mShoppingSet:  [Azúcar,  Leche,  Huevos,  Galletas,  Café]
Se  agregaron  elementos  de  mShoppingSet:  [Azúcar,  Leche,  Huevos,  Galletas,  Café,  Cebollino,  
Espinacas]  Nueva  versión  de  mShoppingList:  [Azúcar,  Leche,  Huevos,  Galletas,  Café,  Cebollino,  Espinacas]

P:  Dijiste  que  puedo  crear  una  Lista P:  ¿ Puedo  ordenar  un  conjunto? P:  Eso  es  inteligente.  ¿Qué  pasa  si  uno  de  los


copia  de  un  Conjunto,  y  una  copia  de   Conjuntos  es  un  MutableSet?  ¿Yo  primero?
Conjunto  de  una  Lista.  ¿Puedo  hacer  algo   R:  No,  un  Set  es  una  colección  desordenada  R:   ¿ Necesitas  copiarlo  a  un  Set?
similar  con  una  matriz? por  lo  que  no  puede  ordenarlo  
directamente.  Sin  embargo,  puede  usar  su   Puedes  usar  ==  sin  copiar
R:  Sí,  puedes.  Las  matrices  tienen  un  montón función  toList()  para  copiar  el  conjunto  en   el  MutableSet  a  un  Conjunto.  En  el
de  funciones  que  puede  usar  para   una  lista  y  luego  ordenar  la  lista. siguiente  ejemplo,  a  ==  b  devuelve  
copiar  la  matriz  en  una  nueva   verdadero:
colección:  toList(),  toMutableList(),   P:  ¿Puedo  usar  el  operador  ==  para
toSet()  y  toMutableSet().  Entonces,   comparar  los  contenidos  de  dos  conjuntos? val  a  =  setOf(1,  2,  3)  val  b  =  
el  siguiente  código  crea  una  matriz  de   mutableSetOf(3,  2,  1)
Ints,  luego  la  copia  en  un  Set<Int>:
R:  Sí,  puedes.  Supongamos  que  tienes  dos
Conjuntos,  a  y  b.  Si  a  y  b  contienen  los  
val  a  =  arrayOf(1,  2,  3)  val  s  =   mismos  valores,  a  ==  b  devolverá  verdadero,   P:  Ya  veo.  Funciona  ==  con  listas
a.toSet() como  en  el  siguiente  ejemplo:
¿también?

De  manera  similar,  List  y  
Set  (y  por  lo  tanto  MutableList  y val  a  =  setOf(1,  2,  3)  val  b  =   R:  Sí,  puedes  usar  ==  para  comparar
setOf(3,  2,  1) //a  ==  b  es   el  contenido  de  dos  Listas.  regresará
MutableSet)  tienen  una  función  llamada  
verdadero verdadero  si  las  listas  tienen  los  mismos  valores
toTypedArray()  que  copia  la  colección  a  
contra  los  mismos  índices,  y  falso  si  las  Listas  
una  nueva  matriz  del  tipo  apropiado.   Sin  embargo,  si  los  dos  conjuntos  comparan   contienen  valores  diferentes,  o  contienen  los  
Entonces  el  código:
valores  diferentes,  el  resultado  será  falso. mismos  valores  en  un  orden  diferente.  así  que  en

el  siguiente  ejemplo,  a  ==  b  devuelve  
val  s  =  setOf(1,  2,  3)  val  a  =  
verdadero:
s.toTypedArray()

crea  una  matriz  de  tipo  Array<Int>. val  a  =  lista  de  (1,  2,  3)  val  b  =  
lista  de  (1,  2,  3)

estas  aqui  4 271
Machine Translated by Google

ser  el  conjunto

SER  el  conjunto  Aquí  
hay  cuatro  clases  de  patos.  Tu  trabajo  es   Esta  es  la  función  principal.
jugar  como  si  fueras  el  Conjunto  y  decir  qué  
clases  producirán  un  Conjunto  que  contenga   diversión  principal(argumentos:  Array<String>)  {
exactamente  un  elemento  cuando   val  set  =  setOf(Pato(),  Pato(17))
se  use  con  la  función  principal  a  
imprimir  (establecer)
la  derecha.  ¿Algún  pato  rompe   }
las  reglas  hashCode()  y  equals()?  
¿Si  es  así,  cómo?

A pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
if  (this  ===  otro)  devuelve  verdadero  if  (otro  es  Pato  
&&  tamaño  ==  otro.tamaño)  devuelve  verdadero  devuelve  falso

anular  fun  hashCode  ():  Int  { tamaño  de  retorno

}
}

B pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
falso  retorno
}

anular  fun  hashCode():  Int  {
volver  7
}
}

C pato  de  clase  de  datos  (tamaño  de  val:  Int  =  18)

D pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
volver  verdadero
}

anular  fun  hashCode():  Int  {
volver  (Math.random()  *  100).toInt()
}
}
Respuestas  en  la  página  274.
272  Capítulo  9
Machine Translated by Google

colecciones

Cuatro  amigos  han  hecho  cada  uno  una  Lista  de  sus  mascotas.  Un  artículo  
en  la  Lista  representa  una  mascota.  Aquí  están  las  cuatro  listas:

val  mascotasLiam  =  listOf("Gato",  "Perro",  "Pez",  "Pez")
val  mascotasSophia  =  listOf("Gato",  "Búho")
val  mascotasNoé  =  listaDe("Perro",  "Paloma",  "Perro",  "Paloma")
val  mascotasEmily  =  listOf("Erizo")

Escriba  el  código  a  continuación  para  crear  una  nueva  colección  llamada  mascotas  que  contenga  cada  mascota.

¿ Cómo  usarías  la  colección  de  mascotas  para  obtener  el  número  total  de  mascotas?

Escriba  el  código  para  imprimir  cuántos  tipos  de  mascotas  hay.

¿Cómo  enumerarías  los  tipos  de  mascotas  en  orden  alfabético?

Respuestas  en  la  página  275.

estas  aqui  4 273
Machine Translated by Google

ser  la  solución  establecida

SER  la  solución  establecida
Aquí  hay  cuatro  clases  de  patos.  Tu  trabajo   Esta  es  la  función  principal.
es  jugar  como  si  fueras  el  Conjunto  y  decir  qué  
clases  producirán  un  Conjunto  que  contenga   diversión  principal(argumentos:  Array<String>)  {

exactamente  un  elemento  cuando   val  set  =  setOf(Pato(),  Pato(17))
se  use  con  la  función  principal  a   imprimir  (establecer)

la  derecha.  ¿Algún  pato  rompe  las   }

reglas  hashCode()  y  equals()?  ¿Si  
es  así,  cómo?

A pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
if  (this  ===  otro)  devuelve  verdadero  if  (otro  es  Pato  
&&  tamaño  ==  otro.tamaño)  devuelve  verdadero  devuelve  falso

}
Esto  sigue  las  reglas  hashCode()  y  equals().  El  

Conjunto  reconoce  que  el  segundo  Pato  es  un  
anular  fun  hashCode  ():  Int  { tamaño  de  retorno
duplicado,  por  lo  que  la  función  principal  crea  un  
Conjunto  que  contiene  un  elemento.
}
}

B pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
falso  retorno
Esto  produce  un  Conjunto  con  dos  elementos.  
}
La  clase  rompe  las  reglas  hashCode()  y  equals()  
anular  fun  hashCode():  Int  { ya  que  equals()  siempre  devuelve  falso,  incluso  si  se  
volver  7
usa  para  comparar  un  objeto  consigo  mismo.
}
}

C pato  de  clase  de  datos  (tamaño  de  val:  Int  =  18) Esto  sigue  las  reglas,  pero  produce  un  Conjunto  con  dos  elementos.

D pato  de  clase  (tamaño  de  valor:  Int  =  17)  {
Esto  produce  un  Conjunto  con  dos  elementos.  
anular  la  diversión  es  igual  a  (otro:  ¿alguno?):  booleano  {
La  clase  rompe  las  reglas  ya  que  hashCode()  
volver  verdadero
} devuelve  un  número  aleatorio.  Las  reglas  dicen  que  

los  objetos  iguales  deben  tener  el  mismo  código  hash.
anular  fun  hashCode():  Int  {
volver  (Math.random()  *  100).toInt()
}
}

274  Capítulo  9
Machine Translated by Google

colecciones

Cuatro  amigos  han  hecho  cada  uno  una  Lista  de  sus  mascotas.  Un  artículo  
en  la  Lista  representa  una  mascota.  Aquí  están  las  cuatro  listas:

val  mascotasLiam  =  listOf("Gato",  "Perro",  "Pez",  "Pez")
val  mascotasSophia  =  listOf("Gato",  "Búho")
val  mascotasNoé  =  listaDe("Perro",  "Paloma",  "Perro",  "Paloma")
val  mascotasEmily  =  listOf("Erizo")

Escriba  el  código  a  continuación  para  crear  una  nueva  colección  llamada  mascotas  que  contenga  cada  mascota.

var  mascotas:  MutableList<String>  =  mutableListOf()  mascotas.addAll(mascotasLiam)
No  se  preocupe  
si  sus  respuestas  
se  ven  diferentes  
a  las  nuestras.   mascotas.addAll(mascotasSophia)

Hay  diferentes  
mascotas.addAll(mascotasNoah)
formas  de  obtener  
el  mismo  resultado.
mascotas.addAll(mascotasEmily)

¿ Cómo  usarías  la  colección  de  mascotas  para  obtener  el  número  total  de  mascotas?

mascotas.tamaño

Escribe  el  siguiente  código  para  imprimir  cuántos  tipos  de  mascotas  hay.

val  petSet  =  mascotas.toMutableSet()  println(petSet.size)

¿Cómo  enumerarías  los  tipos  de  mascotas  en  orden  alfabético?

val  petList  =  petSet.toMutableList()

petList.sort()

println(lista  de  mascotas)

estas  aqui  4 275
Machine Translated by Google

mapas

Hora  de  un  mapa Estos  son  los  valores  del  Mapa.

Las  listas  y  los  conjuntos  son  fantásticos,  pero  hay  otro  tipo  de   “Valor1” “Valor2”


colección  que  queremos  presentarle:  un  mapa.  Un  mapa  es  una  
colección  que  actúa  como  una  lista  de  propiedades.  Le  das  una  clave  y  
el  Mapa  te  devuelve  el  valor  asociado  con  esa  clave.  Aunque  las  claves  
suelen  ser  cadenas,  pueden  ser  cualquier  tipo  de  objeto.
ÁRBITRO ÁRBITRO ÁRBITRO

Cada  entrada  en  un  mapa  es  en  realidad  dos  objetos:  una  clave  y  un  valor.
Cada  clave  tiene  un  único  valor  asociado  a  ella.  Puede  tener  valores  
duplicados,  pero  no  puede  tener  claves  duplicadas.
“Clave  A” “Tecla  B” “Clave  C”

Cómo  crear  un  mapa
Estas  son  las  claves  del  Mapa.
Creas  un  mapa  llamando  a  una  función  llamada  mapOf,  pasando  los  
pares  clave/valor  con  los  que  deseas  que  se  inicialice  el  mapa.
El  siguiente  código,  por  ejemplo,  crea  un  Mapa  con  tres  entradas.  
Las  claves  son  las  cadenas  ("Recipe1",  "Recipe2"  y  "Recipe3"),  y  los  
valores  son  los  objetos  Recipe:

val  r1  =  Receta("Sopa  de  Pollo")
Cada  entrada  toma  la  forma  Clave  de  
val  r2  =  Receta("Ensalada  de  Quinoa")
valor.  Las  claves  son  normalmente  
val  r3  =  Receta  ("Curry  tailandés") cadenas,  como  en  este  ejemplo.

val  recetaMap  =  mapOf("Receta1"  a  r1,  "Receta2"  a  r2,  "Receta3"  a  r3)

Como  era  de  esperar,  el  compilador  infiere  el  tipo  de  los   "Quinua "Tailandés


pares  clave/valor  mirando  las  entradas  con  las  que  se   Ensalada" Curry"
"Pollo
inicializa.  El  mapa  anterior,  por  ejemplo,  se  inicializa  con  
Sopa" Receta Receta
claves  de  cadena  y  valores  de  receta,  por  lo  que  crea  
un  mapa  de  tipo  Map<String,  Recipe>.  También  puede  
Receta
definir  explícitamente  el  tipo  de  mapa  usando  un  código  
como  este: ÁRBITRO ÁRBITRO ÁRBITRO

val  recetaMap:  Map<String,  Receta>

En  general,  el  tipo  de  Mapa  toma  la  forma: “Receta1”  “Receta2”  “Receta3”
ÁRBITRO
Mapa<tipo_clave,  tipo_valor>

receta
Ahora  que  sabe  cómo  crear  un  mapa,  veamos  cómo  
Mapa
usarlo.
valor

Mapa<Cadena,  Receta>

El  tipo  de  clave  es  primero... ...seguido  del  tipo  Valor.

276  Capítulo  9
Machine Translated by Google

colecciones

Cómo  usar  un  mapa
Hay  tres  cosas  principales  que  podría  querer  hacer  con  un  mapa:  
verificar  si  contiene  una  clave  o  valor  específico,  recuperar  un  valor  para  
una  clave  específica  o  recorrer  las  entradas  del  mapa.

Comprueba  si  un  mapa  contiene  una  clave  o  valor  en  particular  
usando  sus  funciones  containsKey  y  containsValue .  El  siguiente  código,  
por  ejemplo,  verifica  si  el  Mapa  llamado  recetaMap  contiene  la  clave  
"Receta1":

recetaMapa.containsKey("Receta1")

Y  puede  averiguar  si  el  mapa  de  recetas  contiene  una  Receta  para
Sopa  de  pollo  usando  la  función  containsValue  de  esta  manera: Aquí,  asumimos  que  Receta  es  una  clase  
de  datos,  por  lo  que  el  Mapa  puede  
val  recetaParaVerificar  =  Receta("Sopa  de  Pollo")
detectar  cuándo  dos  objetos  Receta  son  iguales.
if  (recetaMapa.containsValue(recetaParaVerificar))  {
//Código  que  se  ejecuta  si  el  Mapa  contiene  el  valor
}

Puede  obtener  el  valor  de  una  clave  específica  utilizando  las  
funciones  get  y  getValue .  get  devuelve  un  valor  nulo  si  la  clave  especificada  
no  existe,  mientras  que  getValue  genera  una  excepción.
Así  es  como,  por  ejemplo,  usaría  la  función  getValue  para  obtener  el  objeto  
Receta  asociado  con  la  clave  "Receta1":

if  (recetaMapa.containsKey("Receta1"))  { Si  el  mapa  de  recetas  no  contiene  
val  receta  =  recetaMap.getValue("Receta1") una  clave  "Receta1",  esta  línea  
//Código  para  usar  el  objeto  Receta generará  una  excepción.

También  puede  recorrer  las  entradas  de  un  mapa.  Así  es  como,  
por  ejemplo,  usaría  un  bucle  for  para  imprimir  cada  par  clave/valor  en  el  
mapa  de  recetas:

for  ((clave,  valor)  en  recetaMap)  {
println("La  clave  es  $clave,  el  valor  es  $valor")
}

Un  mapa  es  inmutable,  por  lo  que  no  puede  agregar  ni  quitar  pares  clave/
valor,  ni  actualizar  el  valor  retenido  contra  una  clave  específica.  Para  
realizar  este  tipo  de  acción,  debe  usar  un  MutableMap  en  su  lugar.  Veamos  
cómo  funcionan  estos.

estas  aqui  4 277
Machine Translated by Google

MutableMaps

Crear  un  mapa  mutable
Usted  define  un  MutableMap  de  manera  similar  a  cómo  define  un  
Mapa,  excepto  que  usa  la  función  mutableMapOf  en  lugar  de  mapOf.  
El  siguiente  código,  por  ejemplo,  crea  un  MutableMap  con  tres  
entradas,  como  antes:

val  r1  =  Receta("Sopa  de  Pollo")
val  r2  =  Receta("Ensalada  de  Quinoa")

val  mRecipeMap  =  mutableMapOf("Receta1"  a  r1,  "Receta2"  a  r2)

MutableMap  se  inicializa  con  claves  de  cadena  y  valores  de   "Quinua
"Pollo
receta,  por  lo  que  el  compilador  infiere  que  debe  ser  un   Ensalada"
Sopa"
MutableMap  de  tipo  MutableMap<String,  Recipe>.
Receta
MutableMap  es  un  subtipo  de  Mapa,  por  lo  que  puede  llamar  a  las   Receta
mismas  funciones  en  un  MutableMap  que  en  un  Mapa.  Sin  embargo,  
un  MutableMap  tiene  funciones  adicionales  que  puede  usar  para   ÁRBITRO ÁRBITRO

ÁRBITRO
agregar,  eliminar  y  actualizar  pares  clave/valor.

mReceta
Poner  entradas  en  un  MutableMap Mapa “Receta1”  “Receta2”
Pones  entradas  en  un  MutableMap  usando  la  función  put .   val  MutableMap
El  siguiente  código,  por  ejemplo,  coloca  una  clave  llamada   <Cadena,  Receta>
"Recipe3"  en  mRecipeMap  y  la  asocia  con  un  objeto  Receta  
para  Thai  Curry: Si  pasa  claves  de  cadena  y  valores  de  
receta  a  la  función  mutableMapOf(),  el  
val  r3  =  Receta  ("Curry  tailandés") Especifique  primero   compilador  infiere  que  desea  un  objeto  
la  clave  y  luego  el  valor. de  tipo  MutableMap<String,  Recipe>.
mRecipeMap.put("Receta3",  r3)

Si  MutableMap  ya  contiene  la  clave  especificada,  la  función  put  
reemplaza  el  valor  de  esa  clave  y  devuelve  el  valor  original.

Puede  poner  muchos  pares  clave/valor  en  MutableMap  a  la  vez  usando  la  
función  putAll .  Esto  toma  un  argumento,  un  Mapa  que  contiene  las  
entradas  que  desea  agregar.  El  siguiente  código,  por  ejemplo,  agrega  los  
objetos  Jambalaya  y  Sausage  Rolls  Recipe  a  un  mapa  llamado  
RecipesToAdd  y  luego  coloca  estas  entradas  en  mRecipeMap:

val  r4  =  Receta("Jambalaya")
val  r5  =  Receta("Rollos  De  Salchicha")
val  recetasParaAgregar  =  mapOf("Receta4"  a  r4,  "Receta5"  a  r5)
mRecipeMap.putAll(recetasParaAgregar)

A  continuación,  veamos  cómo  elimina  valores.

278  Capítulo  9
Machine Translated by Google

colecciones

Puede  eliminar  entradas  de  un  MutableMap
Elimina  una  entrada  de  un  MutableMap  usando  la  función  de  eliminación .  
Esta  función  está  sobrecargada,  por  lo  que  hay  dos  formas  de  llamarla.

La  primera  forma  es  pasar  a  la  función  eliminar  la  clave  cuya  
entrada  desea  eliminar.  El  siguiente  código,  por  ejemplo,  elimina  
la  entrada  de  mRecipeMap  que  tiene  una  clave  de  "Recipe2":

mRecetaMap.remove("Receta2") Eliminar  la  entrada  con  una  clave  de  "Receta2"

La  segunda  forma  es  pasar  a  la  función  de  eliminación  el  nombre  de  la  
clave  y  un  valor.  En  este  caso,  la  función  solo  eliminará  la  entrada  si  
encuentra  una  coincidencia  tanto  para  la  clave  como  para  el  valor.  Por  lo  
tanto,  el  siguiente  código  solo  elimina  la  entrada  de  "Receta2"  si  está  
asociada  con  un  objeto  Receta  de  ensalada  de  quinoa:

val  recetaParaEliminar  =  Receta("Ensalada  de  Quinoa") Elimine  la  entrada  con  una  clave  de  
mRecipeMap.remove("Receta2",  recetaParaEliminar) "Receta2",  pero  solo  si  su  valor  es  un  
objeto  Receta  de  ensalada  de  quinoa.
Cualquiera  que  sea  el  enfoque  que  utilice,  eliminar  una  entrada  de  
MutableMap  reduce  su  tamaño.

Finalmente,  puede  usar  la  función  borrar  para  eliminar  todas  las  entradas  
de  MutableMap,  tal  como  puede  hacerlo  con  MutableLists  y  MutableSets:

mRecetaMap.clear()
Oye,  ¿adónde  
fueron  todos?

ÁRBITRO

mReceta
Mapa La  función  clear()  elimina  todas  

las  entradas,  pero  el  objeto  MutableMap  
val  MutableMap
en  sí  todavía  existe.
<Cadena,  Receta>

Ahora  que  ha  visto  cómo  actualizar  un  MutableMap,  veamos  
cómo  puede  hacer  copias  de  uno.

estas  aqui  4 279
Machine Translated by Google

copiando  mapas

Puedes  copiar  Maps  y  MutableMaps
Al  igual  que  los  otros  tipos  de  colección  que  ha  visto,  puede  
tomar  una  instantánea  de  un  MutableMap.  Puede  usar  la  función  
toMap ,  por  ejemplo,  para  tomar  una  copia  de  solo  lectura  de  
mRecipeMap  y  asignar  la  copia  a  una  nueva  variable:

val  recetaMapCopy  =  mRecipeMap.toMap()

Puede  copiar  un  mapa  o  mapa  mutable  en  un  nuevo  objeto  de  lista  que  
Un  MutableMap  también  tiene  las  funciones  
contenga  todos  los  pares  clave/valor  usando  toList  de  esta  manera: toMutableMap()  y  toMutableList().

val  Lista  de  Recetas  =  mRecipeMap.toList()

Y  también  puede  obtener  acceso  directo  a  los  pares  clave/valor  utilizando  la  
propiedad  de  entradas  del  mapa .  La  propiedad  de  entradas  devuelve  un  
conjunto  si  se  usa  con  un  mapa  y  devuelve  un  conjunto  mutable  si  se  usa  con  
un  mapa  mutable.  El  siguiente  código,  por  ejemplo,  devuelve  un  MutableSet  
de  pares  clave/valor  de  mRecipeMap:

val  recetasEntries  =  mRecipeMap.entries
Tenga  en  cuenta  que  las  propiedades  de  
Otras  propiedades  útiles  son  las  claves  (que  devuelve  un  Conjunto,   entradas,  claves  y  valores  son  los  
o  MutableSet,  de  las  claves  del  Mapa)  y  los  valores  (que  devuelve  una   contenidos  reales  del  Mapa  o  MutableMap.  
colección  genérica  de  los  valores  del  Mapa).  Puede  usar  estas  propiedades   No  son  copias.  Y  si  está  utilizando  un  
para,  por  ejemplo,  verificar  si  un  mapa  contiene  valores  duplicados  usando   MutableMap,  estas  propiedades  son  actualizables.
un  código  como  este:

if  (mRecipeMap.tamaño  >  mRecipeMap.valores.toSet().tamaño)  {
println("mRecipeMap  contiene  valores  duplicados")
}

Esto  se  debe  a  que  el  código:

mRecipeMap.valores.toSet()

copia  los  valores  del  mapa  en  un  conjunto,  que  elimina  los  valores  duplicados.

Ahora  que  ha  aprendido  a  usar  Maps  y  MutableMaps,  agreguemos  
algunos  a  nuestro  proyecto  Colecciones.

280  Capítulo  9
Machine Translated by Google

colecciones

El  código  completo  para  el  proyecto  Colecciones
Actualice  su  versión  de  Collections.kt  para  que  coincida  con  la  nuestra  a  

continuación  (nuestros  cambios  están  en  negrita):

clase  de  datos  Receta  (var  nombre:  Cadena) Agregue  la  clase  de  datos  Receta.

diversión  principal(argumentos:  Array<String>)  {
var  mShoppingList  =  mutableListOf("Té",  "Huevos",  "Leche")  println("mShoppingList  
original:  $mShoppingList")  val  extraShopping  =  listOf("Galletas",  "Azúcar",  "Huevos")  
mShoppingList.addAll(extraShopping)  println("artículos  de  mShoppingList  agregados:  
$mShoppingList")  if  (mShoppingList.contains("Tea"))  
{ mShoppingList.set(mShoppingList.indexOf("Tea"),  "Coffee")

}
mShoppingList.sort()  
println("mShoppingList  ordenada:  $mShoppingList")  mShoppingList.reverse()  
println("mShoppingList  invertida:  $mShoppingList")

Colecciones

val  mShoppingSet  =  mShoppingList.toMutableSet()  println("mShoppingSet:  
origen
$mShoppingSet")  val  másCompras  =  setOf("Cebollino",  "Espinacas",  
"Leche")  mShoppingSet.addAll(másCompras)  println("mShoppingSet  elementos  
agregados:  $mShoppingSet" )  mShoppingList  =  mShoppingSet.toMutableList()   Colecciones.kt
println("mShoppingList  nueva  versión:  $mShoppingList")

val  r1  =  Receta("Sopa  de  Pollo")  val  r2  =  
Receta("Ensalada  de  Quinoa")  val  r3  =  
Receta("Curry  Tailandés")  val  r4  =  
Receta("Jambalaya")  val  r5  =  Receta("Rollitos  de  
Salchicha")  val  mRecipeMap  =  
Agrega  este  
mutableMapOf("Receta1"  a  r1,  "Receta2"  a  r2,  "Receta3"  a  r3)  println("mRecipeMap  original:  $mRecipeMap")  val  
código.
recetasParaAgregar  =  mapOf("Receta4"  a  r4,  "Receta5"  a  r5)  mRecipeMap .putAll(recetasParaAgregar)  println("mRecipeMap  
actualizado:  $mRecipeMap")  if  (mRecipeMap.containsKey("Recipe1"))  {

println("Receta1  es:  ${mRecetaMap.getValue("Receta1")}")
}
}

Tomemos  el  código  para  una  prueba  de  manejo.

estas  aqui  4 281
Machine Translated by Google

prueba  de  manejo

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  el
Ventana  de  salida  del  IDE:

mShoppingList  original:  [Té,  Huevos,  Leche]  
mShoppingList  elementos  agregados:  [Té,  Huevos,  Leche,  Galletas,  Azúcar,  
Huevos]  mShoppingList  ordenada:  [Café,  Galletas,  Huevos,  Huevos,  Leche,  
Azúcar]  mShoppingList  invertida:  [Azúcar,  Leche ,  Huevos,  Huevos,  Galletas,  
Café]  mShoppingSet:  [Azúcar,  Leche,  Huevos,  Galletas,  Café]  Elementos  de  
mShoppingSet  agregados:  [Azúcar,  Leche,  Huevos,  Galletas,  Café,  Cebollino,  Espinacas]  
mShoppingList  nueva  versión:  [Azúcar,  Leche,  Huevos,  Galletas,  Café,  Cebollino,  Espinacas]  
mRecipeMap  original:  {Recipe1=Recipe(name=Pollo  Sopa),  Recipe2=Receta(name=Ensalada  de  Quinoa),
Receta3=Receta(nombre=Curry  tailandés)}
mRecipeMap  actualizado:  {Receta1=Receta(nombre=Sopa  de  pollo),  Receta2=Receta(nombre=Ensalada  de  quinua),
Receta3=Receta(nombre=Curry  tailandés),  Receta4=Receta(nombre=Jambalaya),
Receta5=Receta(nombre=Rollos  De  Salchicha)}
La  impresión  de  un  Mapa  o  MutableMap  
Receta1  es:  Receta(nombre=Sopa  de  Pollo) imprime  cada  par  clave/valor  dentro  de  llaves.

P:  ¿ Por  qué  Kotlin  tiene  mutable P:  ¿Es  posible  crear  un P:  ¿ Es  eso  diferente  a  usar  toSet?


y  versiones  inmutables  del  mismo   vista  no  actualizable  de  una  colección  
tipo  de  colección?  ¿Por  qué  no  tener   mutable?
R:  Sí.  toSet  copia  una  colección,
versiones  mutables?
por  lo  tanto,  si  se  realizan  cambios  en  la  
R:  Supongamos  que  tiene  un colección  original,  estos  no  se  recuperarán.
R:  Porque  te  obliga  a  explícitamente MutableSet  of  Ints  que  se  asigna  a  
elige  si  tu  colección  debe  ser  mutable   una  variable  llamada  x: P:  ¿ Puedo  crear  y  usar  explícitamente
o  inmutable.  Esto  significa  que  puede  
¿Colecciones  de  Java  usando  Kotlin?
evitar  que  las  colecciones  se  actualicen  
valor  x  =  mutableSetOf(1,  2)
si  no  desea  que  se  actualicen.
R:  Sí.  Kotlin  incluye  varios
Puede  asignar  x  a  una  variable  Set   funciones  que  le  permiten  crear  
P:  ¿ No  puedo  hacer  eso  usando  val  y llamada  y  usando  el  siguiente  código: explícitamente  colecciones  Java.  
var? Puede,  por  ejemplo,  crear  un  ArrayList  
valor  y:  Set<Int>  =  x usando  la  función  arrayListOf  y  un  
A:  No.  val  y  var  especifican  si HashMap  usando  la  función  
o  no,  la  referencia  que  tiene  la  variable  se   Como  y  es  una  variable  Set ,  no  puede   hashMapOf .  Estas  funciones,  sin  
puede  reemplazar  por  otra  después  de  que   actualizar  el  objeto  subyacente  sin  que  primero   embargo,  crean  objetos  mutables.
se  haya  inicializado.  Si  una  variable   lo  convierta  en  un  MutableSet.
definida  con  val  contiene  una  referencia  a   Le  recomendamos  que  siga  usando  las  
una  colección  mutable,  la  colección  aún  se   colecciones  de  Kotlin  que  hemos  discutido  en  

puede  actualizar.  val  solo  significa  que  la   este  capítulo,  a  menos  que  haya  una  buena  
variable  solo  puede  referirse  a  esa  colección. razón  por  la  que  no  debería  hacerlo.

282  Capítulo  9
Machine Translated by Google

colecciones

Rompecabezas  de  piscina

diversión  principal(argumentos:  Array<String>)  { Su  trabajo  es  tomar  fragmentos  de  código  del  
val  term1  =  "Array" grupo  y  colocarlos  en  las  líneas  en  blanco  
val  term2  =  "Lista" del  código.  No  puede  usar  el  mismo  
fragmento  de  código  más  de  una  vez  y  no  
val  term3  =  "Mapa"
necesitará  usar  todos  los  fragmentos  de  
val  term4  =
código.  Su  objetivo  es  imprimir  las  
val  term5  =  "MapaMutable" entradas  de  un  mapa  llamado  glosario  
val  term6  =  "ConjuntoMutable" que  proporciona  definiciones  de  todos  los  
val  term7  =  "Establecer" tipos  de  colecciones  que  ha  aprendido.

val  def1  =  "Mantiene  los  valores  sin  ningún  orden  en  particular".  val  def2  =  
"Contiene  pares  clave/valor".
val  def3  =  "Retiene  valores  en  una  secuencia".
val  def4  =  "Se  puede  actualizar".
val  def5  =  "No  se  puede  actualizar".
val  def6  =  "Se  puede  cambiar  el  tamaño".

val  def7  =  "No  se  puede  cambiar  el  tamaño".

glosario  val  =  a  "$def1   ( a  "$def3  $def4  $def6",
$def5  $def7",  a  "$def3  $def4  $def7",  
a  "$def2  $def4  $def6",  a  "$def3  $def5  
$def7",  a  "$def1  $def4  $  def6",  a  
"$def2  $def5  $def7")

for  ((clave,  valor)  en  el  glosario)  println("$clave:  $valor")
}

Nota:  ¡cada  cosa  del  grupo  
solo  se  puede  usar  una  vez!

"Matriz  Mutable"

"ListaMutable" termino  1
Mapa term4
term2
mapa  de term5
term3 term6

term7

estas  aqui  4 283
Machine Translated by Google

solución  de  rompecabezas  de  piscina

Solución  de  rompecabezas  de  piscina
diversión  principal(argumentos:  Array<String>)  { Su  trabajo  es  tomar  fragmentos  de  código  del  
val  term1  =  "Array" grupo  y  colocarlos  en  las  líneas  en  blanco  
val  term2  =  "Lista" del  código.  No  puede  usar  el  mismo  
fragmento  de  código  más  de  una  vez  y  
val  term3  =  "Mapa"
no  necesitará  usar  todos  los  fragmentos  
val  term4  = "ListaMutable"
de  código.  Su  objetivo  es  imprimir  las  
val  term5  =  "MapaMutable" entradas  de  un  mapa  llamado  glosario  
val  term6  =  "ConjuntoMutable" que  proporciona  definiciones  de  todos  los  
val  term7  =  "Establecer" tipos  de  colecciones  que  ha  aprendido.

val  def1  =  "Mantiene  los  valores  sin  ningún  orden  en  particular".  val  def2  =  
"Contiene  pares  clave/valor".
val  def3  =  "Retiene  valores  en  una  secuencia".
val  def4  =  "Se  puede  actualizar".
val  def5  =  "No  se  puede  actualizar".
val  def6  =  "Se  puede  cambiar  el  tamaño".

val  def7  =  "No  se  puede  cambiar  el  tamaño".

glosario  val  =  mapOf  a  "$def1   ( term4 a  "$def3  $def4  $def6",


term7 $def5  $def7",  a  "$def3  $def4  $def7",  
termino  1 a  "$def2  $def4  $def6",  a  "$def3  
term5 $def5  $def7",  a  "$def1  $def4  $def6",  
term2 a  "$def2  $def5  $def7")
term6
term3
for  ((clave,  valor)  en  el  glosario)  println("$clave:  $valor")
}

No  necesitabas  
usar  estos  fragmentos.
"Matriz  Mutable"

Mapa

284  Capítulo  9
Machine Translated by Google

colecciones

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
diversión  principal(argumentos:  Array<String>)  {
val  mList  =  mutableListOf("Fútbol",  "Béisbol",  "Baloncesto")
El  código  de  
candidato  va  aquí.

Relaciona  
cada  candidato  
con  uno  de  los  
}
posibles  resultados.

Candidatos: Salida  posible:

mList.sort()
[Baloncesto]
println(mLista)

[Béisbol,  Baloncesto,  Fútbol]
val  mMap  =  mutableMapOf("0"  a  "Netball")
var  x  =  0 [Baloncesto]
para  (elemento  en  mlist)  {
[Fútbol,  Baloncesto,  Béisbol]
mMap.put(x.toString(),  elemento)
}
{Baloncesto}
println(mMap.valores)

[Baloncesto,  Béisbol,  Fútbol]
mList.addAll(mList)
mList.reverse() {Baloncesto}

val  conjunto  =  mlist.toSet()
[Fútbol  americano]
imprimir  (establecer)

{Baloncesto,  Béisbol,  Fútbol}
mList.sort()
mList.reverse() [Fútbol,  Béisbol,  Baloncesto]
println(mLista)

estas  aqui  4 285
Machine Translated by Google

solución  de  mensajes  mixtos

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
Solución
diversión  principal(argumentos:  Array<String>)  {
val  mList  =  mutableListOf("Fútbol",  "Béisbol",  "Baloncesto")
El  código  de  
candidato  va  aquí.

Candidatos: Salida  posible:

mList.sort()
[Baloncesto]
println(mLista)

[Béisbol,  Baloncesto,  Fútbol]
val  mMap  =  mutableMapOf("0"  a  "Netball")
var  x  =  0 [Baloncesto]
para  (elemento  en  mlist)  {

mMap.put(x.toString(),  elemento) [Fútbol,  Baloncesto,  Béisbol]

}
{Baloncesto}
println(mMap.valores)

[Baloncesto,  Béisbol,  Fútbol]
mList.addAll(mList)
mList.reverse() {Baloncesto}

val  conjunto  =  mlist.toSet()
[Fútbol  americano]
imprimir  (establecer)

{Baloncesto,  Béisbol,  Fútbol}
mList.sort()
mList.reverse() [Fútbol,  Béisbol,  Baloncesto]
println(mLista)

286  Capítulo  9
Machine Translated by Google

colecciones

Tu  caja  de  herramientas  de  Kotlin

CAPÍTULO  
9 Tiene  el  Capítulo  9  en  su  haber  y  ahora  ha  
agregado  colecciones  a  su  caja  de  herramientas.
Puede  descargar  
el  código  completo  
del  capítulo  desde  
https://tinyurl.com/
HFKotlin.

Cree  una  matriz  inicializada  con  valores   Crea  una  lista  usando  la  función  listOf.
nulos  mediante  la  función  arrayOfNulls.

Cree  una  MutableList  usando  
Las  funciones  de  matriz  útiles  incluyen:   mutableListOf.
ordenar,  invertir,  contiene,  min,  max,
Cree  un  conjunto  usando  la  función  setOf.
suma,  promedio.
Cree  un  MutableSet  usando  
La  biblioteca  estándar  de  Kotlin  contiene  clases  
mutableSetOf.
y  funciones  prediseñadas  agrupadas  en  
paquetes. Un  conjunto  busca  duplicados  buscando  
primero  valores  de  código  hash  coincidentes.  
Una  lista  es  una  colección  que  conoce  y  se  
Luego  usa  los  operadores  ===  y  ==  para  
preocupa  por  la  posición  del  índice.  Puede  
verificar  la  igualdad  referencial  o  de  objeto.
contener  valores  duplicados.
Cree  un  mapa  usando  la  función  mapOf,  
Un  Conjunto  es  una  colección  desordenada  
pasando  pares  clave/valor.
que  no  permite  valores  duplicados.
Cree  un  MutableMap  usando  
Un  mapa  es  una  colección  que  utiliza  pares  
mutableMapOf.
clave/valor.  Puede  contener  valores  duplicados,  
pero  no  claves  duplicadas.

List,  Set  y  Map  son  inmutables.
MutableList,  MutableSet  y  MutableMap  son  
subtipos  mutables  de  estas  colecciones.

estas  aqui  4 287
Machine Translated by Google
Machine Translated by Google

10 genéricos

Saber Su
Entradas  desde Su salidas
Cariño,  me  temo  que  
la  T  en  Meat<T>  puede  
implementar  la  interfaz  
Tabby.

A  todo  el  mundo  le  gusta  el  código  que  es  consistente.
Y  una  forma  de  escribir  código  consistente  que  sea  menos  propenso  a  problemas  es  usar  genéricos.

En  este  capítulo,  veremos  cómo  las  clases  de  colección  de  Kotlin  usan  genéricos  para  evitar  que  

coloques  un  repollo  en  una  lista  <Seagull>.  Descubrirá  cuándo  y  cómo  escribir  sus  propias  clases,  

interfaces  y  funciones  genéricas,  y  cómo  restringir  un  tipo  genérico  a  un  supertipo  específico.  

Finalmente,  descubrirá  cómo  usar  la  covarianza  y  la  contravarianza,  lo  que  le  permite  a  USTED  

controlar  el  comportamiento  de  su  tipo  genérico.

este  es  un  nuevo  capitulo  289
Machine Translated by Google

los  genéricos  permiten  la  coherencia  de  tipos

Las  colecciones  usan  genéricos

Como  aprendiste  en  el  capítulo  anterior,  cada  vez  que  declaras  
explícitamente  el  tipo  de  una  colección,  debes  especificar  tanto  el  
tipo  de  colección  que  deseas  usar  como  el  tipo  de  elemento  que  contiene.
El  siguiente  código,  por  ejemplo,  define  una  variable  que  puede  
contener  una  referencia  a  una  MutableList  of  Strings:

val  x:  MutableList<Cadena>

El  tipo  de  elemento  se  define  entre  paréntesis  angulares  <>,  lo  
que  significa  que  utiliza  genéricos.  Generics  le  permite  escribir  código  
con  seguridad  de  tipos.  Es  lo  que  hace  que  el  compilador  le  impida  
poner  un  Volkswagen  en  una  lista  de  patos.  El  compilador  sabe  que  
solo  puede  colocar  objetos  Duck  en  una  MutableList<Duck>,  lo  que  
significa  que  se  detectan  más  problemas  en  el  momento  de  la  compilación.

SIN  genéricos,  los  objetos  
entrarían  como  referencia  a  los  
objetos  Pato,  Pez,  Guitarra  y  Coche...
Pato Pez Guitarra Auto

Sin  genéricos,  no  
MutableList habría  manera  de  
declarar  qué  tipo  
de  objetos  debería  
contener  MutableList.

Cualquier Cualquier Cualquier Cualquier

...y  sale  como  una  referencia  de  tipo  Cualquiera.

CON  genéricos,  los  
objetos  entran  como  una  
Con  los  genéricos,  puede  
referencia  solo  a  los   asegurarse  de  que  su  
objetos  Duck... Pato Pato Pato Pato
colección  solo  contenga  
objetos  del  tipo  correcto.  No  
tienes  que  preocuparte  de  
ListaMutable<Pato> que  alguien  meta  una  
Calabaza  en  un  
MutableList<Duck>,  o  que  
lo  que  obtengas  no  sea  un  Pato.

Pato Pato Pato Pato

...y  sale  como  una  referencia  de  tipo  Pato.

290  Capítulo  10
Machine Translated by Google

genéricos

Cómo  se  define  una  MutableList
Miremos  la  documentación  en  línea  para  ver  cómo  se  
define  MutableList  y  cómo  usa  genéricos.  Hay  dos  áreas  clave  que  
consideraremos:  la  declaración  de  la  interfaz  y  cómo  se  define  la  
función  de  agregar.

Comprender  la  documentación  de  la  
colección  (o,  ¿cuál  es  el  significado  de  "E"?)
Aquí  hay  una  versión  simplificada  de  la  definición  MutableList:

MutableList  hereda  de  las  interfaces  List  y  
MutableCollection.  Cualquier  tipo  (el  valor  de  
La  "E"  es  un  marcador  de   "E")  que  especifique  para  MutableList  se  usa  
posición  para  el  tipo  REAL  que   automáticamente  para  el  tipo  de  List  y  
usa  cuando  declara  una  MutableList. MutableCollection.

interfaz  MutableList<E> :  List<E>,  MutableCollection<E>  {

divertido  agregar  (índice:  Int,  elemento:  E):  Unidad

Cualquiera  que  sea  la  "E"  
//Más  código
determina  qué  tipo  de  cosas  
puede  agregar  a  MutableList.
}

MutableList  usa  "E"  como  sustituto  del  tipo  de  elemento  que  
desea  que  la  colección  contenga  y  devuelva.
Cuando  vea  una  "E"  en  la  documentación,  puede  buscar/
reemplazar  mentalmente  para  cambiarla  por  el  tipo  que  desee   P:  ¿ Entonces  MutableList  no  es  una  clase?
que  contenga.
R:  No,  es  una  interfaz.  Cuando  creas  un
MutableList<String>,  por  ejemplo,  significa  que  "E"  se  
MutableList  usando  la  función  mutableListOf ,  el  
convierte  en  "String"  en  cualquier  función  o  declaración  de  
sistema  crea  una  implementación  de  esta  
variable  que  use  "E".  Y  MutableList<Duck>  significa  que  todas  
interfaz.  Sin  embargo,  todo  lo  que  le  importa  
las  instancias  de  "E"  se  convierten  en  "Duck"  en  su  lugar.
cuando  lo  usa  es  que  tiene  todas  las  propiedades  
Veamos  esto  con  más  detalle. y  funciones  definidas  en  la  interfaz  MutableList .

estas  aqui  4 291
Machine Translated by Google

cómo  funcionan  los  parámetros  de  tipo

Uso  de  parámetros  de  tipo  con  MutableList
Cuando  escribes  este  código:

val  x:  MutableList<Cadena>

Significa  que  MutableList:

interfaz  MutableList<E> :  List<E>,  MutableCollection<E>  {

divertido  agregar  (índice:  Int,  elemento:  E):  Unidad

//Más  código

es  tratado  por  el  compilador  como:

interfaz  MutableList<String> :  List<String>,  MutableCollection<String>  {

divertido  agregar  (índice:  Int,  elemento:  Cadena):  Unidad

//Más  código

En  otras  palabras,  la  "E"  se  reemplaza  por  el  tipo  real  (también  
llamado  parámetro  de  tipo)  que  usa  cuando  define  MutableList.  
Y  es  por  eso  que  la  función  de  agregar  no  le  permitirá  agregar  
nada  excepto  objetos  con  un  tipo  que  sea  compatible  con  el  tipo  de  
"E".  Entonces,  si  crea  una  MutableList<String>,  la  función  de  agregar  
de  repente  le  permite  agregar  cadenas.  Y  si  crea  MutableList  de  tipo  
Duck,  de  repente  la  función  de  agregar  le  permite  agregar  Ducks.

292  Capítulo  10
Machine Translated by Google

genéricos

Cosas  que  puede  hacer  con  
una  clase  o  interfaz  genérica
Aquí  hay  un  resumen  de  algunas  de  las  cosas  clave  que  
puede  hacer  cuando  usa  una  clase  o  interfaz  que  tiene  tipos  
genéricos:

¥ Cree  una  instancia  de  una  clase  generada.
Cuando  crea  una  colección  como  MutableList,  debe  indicarle  el  tipo  
de  objetos  que  permitirá  que  contenga,  o  dejar  que  el  compilador  lo  
deduzca:

val  lista  de  patos:  lista  mutable<pato>

patoLista  =  mutableListOf(Pato("Donald"),  Pato("Daisy"),  Pato("Lucas"))

val  list  =  mutableListOf("Tarifa",  "Fi",  "Fum")

¥ Cree  una  función  que  tome  un  tipo  genérico.
Puede  crear  una  función  con  un  parámetro  genérico  especificando  su  
tipo,  tal  como  lo  haría  con  cualquier  otro  tipo  de  parámetro:

graznido  divertido  (patos:  MutableList<Duck>)  {

//Código  para  hacer  graznar  a  los  Patos

¥ Cree  una  función  que  devuelva  un  tipo  genérico.
Una  función  también  puede  devolver  un  tipo  genérico.  El  siguiente  
código,  por  ejemplo,  devuelve  una  MutableList  of  Ducks:

diversión  getDucks(raza:  String):  MutableList<Duck>  {

//Código  para  obtener  patos  para  la  raza  especificada

Pero  todavía  hay  preguntas  importantes  que  deben  responderse  
sobre  los  genéricos,  como  ¿cómo  define  sus  propias  clases  e  
interfaces  genéricas?  ¿Y  cómo  funciona  el  polimorfismo  con  los  
tipos  genéricos?  Si  tiene  un  MutableList<Animal>,  ¿qué  sucede  si  
intenta  asignarle  un  MutableList<Dog>?

Para  responder  estas  preguntas  y  más,  vamos  a  crear  una  aplicación  
que  use  tipos  genéricos.

estas  aqui  4 293
Machine Translated by Google

pasos

Esto  es  lo  que  vamos  a  hacer
Vamos  a  crear  una  aplicación  que  se  ocupe  de  las  mascotas.  Crearemos  algunas  
mascotas,  organizaremos  concursos  de  mascotas  para  ellas  y  crearemos  minoristas  
de  mascotas  que  puedan  vender  tipos  específicos  de  mascotas.  Y  como  estamos  
usando  genéricos,  nos  aseguraremos  de  que  cada  concurso  y  minorista  que  creemos  
solo  pueda  tratar  con  un  tipo  específico  de  mascota.

Estos  son  los  pasos  que  seguiremos:

1 Cree  la  jerarquía  de  mascotas.
Crearemos  una  jerarquía  de  mascotas  que  nos  permitirá  crear  tres  tipos  de  
mascotas:  gatos,  perros  y  peces.

2 Cree  la  clase  Concurso.
La  clase  Contest  nos  permitirá  crear  concursos  para  diferentes  tipos  de  mascotas.
Lo  usaremos  para  administrar  las  puntuaciones  de  los  concursantes  para  que  
podamos  determinar  el  ganador.  Y  como  queremos  que  cada  concurso  se  limite  a  un  
tipo  específico  de  mascota,  definiremos  la  clase  Concurso  usando  genéricos.

3 Cree  la  jerarquía  de  minoristas.
Crearemos  una  interfaz  para  minoristas  e  implementaciones  concretas  de  
esta  interfaz  denominadas  CatRetailer,  DogRetailer  y  FishRetailer.  Usaremos  
genéricos  para  garantizar  que  cada  tipo  de  minorista  solo  pueda  vender  un  
tipo  específico  de  mascota,  de  modo  que  no  pueda  comprar  un  gato  de  un  
FishRetailer.

4 Crear  una  clase  de  veterinario.

Finalmente,  crearemos  una  clase  Vet,  para  que  podamos  asignar  un  veterinario  a  cada  
concurso.  Definiremos  la  clase  de  veterinario  utilizando  genéricos  para  reflejar  el  tipo  
de  mascota  en  el  que  se  especializa  cada  veterinario.

Comenzaremos  creando  la  jerarquía  de  clases  de  mascotas.

294  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Crear  la  jerarquía  de  clases  de  mascotas minoristas
Veterinario

Nuestra  jerarquía  de  clases  de  mascotas  constará  de  cuatro  clases:  una  
clase  de  mascotas  que  marcaremos  como  subclases  abstractas  y  concretas  
denominadas  gato,  perro  y  pez.  Agregaremos  una  propiedad  de  nombre  a  la  
clase  Pet,  que  heredarán  sus  subclases  concretas.

Estamos  marcando  Mascota  como  abstracta  porque  solo  queremos  poder  
crear  objetos  que  sean  un  subtipo  de  Mascota,  como  Gato  o  Perro,  y  como  
aprendiste  en  el  Capítulo  6,  marcar  una  clase  como  abstracta  evita  que  esa  
clase  sea  instanciada.

Aquí  está  la  jerarquía  de  clases:

Mascota

Aquí,  Pet  es  abstracto,  mientras  
nombre
que  Cat,  Dog  y  Fish  son  
implementaciones  concretas.

Gato Perro Pez

El  código  para  la  jerarquía  de  clases  se  ve  así:

clase  abstracta  Mascota  (var  nombre:  Cadena)

clase  Gato(nombre:  Cadena) :  Mascota(nombre)
Cada  subtipo  de  Pet  tiene  un  
nombre  (que  hereda  de  Pet),  que  se  
clase  Perro(nombre:  Cadena) :  Mascota(nombre)
establece  en  el  constructor  de  la  clase.

clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)

A  continuación,  creemos  la  clase  Concurso  para  que  podamos  realizar  
concursos  para  diferentes  tipos  de  mascotas.

295
estas  aqui  4
Machine Translated by Google

clase  de  concurso

Mascotas

Concurso
Definir  la  clase  Contest minoristas
Veterinario

Usaremos  la  clase  Concurso  para  ayudarnos  a  administrar  los  puntajes  de  un  
concurso  de  mascotas  y  determinar  el  ganador.  La  clase  tendrá  una  propiedad  
llamada  puntuaciones  y  dos  funciones  llamadas  addScore  y  getWinners.

Queremos  que  cada  concurso  se  limite  a  un  tipo  particular  de  mascota.  Un  concurso  
de  gatos,  por  ejemplo,  solo  tiene  concursantes  de  gatos,  y  solo  los  peces  pueden  
participar  en  un  concurso  de  peces.  Haremos  cumplir  esta  regla  usando  genéricos.

Declarar  que  Contest  usa  un  tipo  genérico
Usted  especifica  que  una  clase  usa  un  tipo  genérico  poniendo  el  nombre  del  tipo  entre  
paréntesis  angulares  inmediatamente  después  del  nombre  de  la  clase.  Aquí,  usaremos  
"T"  para  indicar  el  tipo  genérico.  Puede  pensar  en  "T"  como  un  sustituto  del  tipo  real  
con  el  que  tratará  cada  objeto  del  Concurso.

Aquí  está  el  código:
La  <T>  después  del  nombre  
de  la  clase  le  dice  al   Concurso<T>
Concurso  de  clase<T>  {
compilador  que  T  es  un  tipo  genérico.
//Más  código  aquí

El  nombre  de  tipo  genérico  puede  ser  cualquier  identificador  legal,  pero  la  
convención  (que  debe  seguir)  es  usar  "T".  La  excepción  es  si  está  escribiendo  
una  clase  o  interfaz  de  colección,  en  cuyo  caso  la  convención  es  usar  "E"  en  su  lugar  
(para  "Elemento"),  o  "K"  y  "V" (para  "Clave"  y  "Valor" )  si  es  un  mapa.

Puede  restringir  T  a  un  supertipo  específico
En  el  ejemplo  anterior,  T  se  puede  reemplazar  por  cualquier  tipo  real  cuando  se  
instancia  la  clase.  Sin  embargo,  puede  imponer  restricciones  a  T  especificando  que  
tiene  un  tipo.  El  siguiente  código,  por  ejemplo,  le  dice  al  compilador  que  T  debe  ser  
un  tipo  de  Mascota:

clase  Concurso<T:  Mascota>  { T  es  un  tipo  genérico   Concurso<T:  Mascota>

//Más  código  aquí que  debe  ser  Pet,  o  de  
uno
} sus  subtipos.

Entonces,  el  código  anterior  significa  que  puede  crear  objetos  de  Concurso  que  se  
ocupen  de  Gatos,  Peces  o  Mascotas,  pero  no  de  Bicicletas  o  Begonias.

A  continuación,  agreguemos  la  propiedad  de  puntajes  a  la  clase  Contest.

296  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Agregar  la  propiedad  de  puntuaciones minoristas
Veterinario

La  propiedad  de  puntajes  necesita  realizar  un  seguimiento  de  qué  
concursante  recibe  qué  puntaje.  Por  lo  tanto,  usaremos  un  MutableMap,  
con  los  concursantes  como  claves  y  sus  puntajes  como  valores.  Como  
cada  concursante  es  un  objeto  de  tipo  T  y  cada  puntuación  es  un  Int,  la  
propiedad  de  puntuaciones  tendrá  un  tipo  de  MutableMap<T,  Int>.  Si  creamos  
un  Contest<Cat>  que  se  ocupa  de  los  concursantes  Cat,  el  tipo  de  propiedad  
de  puntuaciones  se  convertirá  en  MutableMap<Cat,  Int>,  pero  si  creamos  un  
objeto  Contest<Pet>,  el  tipo  de  puntuaciones  se  convertirá  automáticamente  
en  MutableMap<Pet,  Int>  en  su  lugar .

Aquí  está  el  código  actualizado  para  la  clase  Contest:

clase  Concurso<T:  Mascota>  { Concurso<T:  Mascota>

puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf() puntuaciones

//Más  código  aquí
Esto  define  un  MutableMap  con  claves  T  
}
y  valores  Int,  donde  T  es  el  tipo  genérico  de  
Mascota  con  el  que  se  trata  el  Concurso.

Ahora  que  hemos  agregado  la  propiedad  de  puntuaciones,  
agreguemos  las  funciones  addScore  y  getWinners.

Crear  la  función  addScore
Queremos  que  la  función  addScore  agregue  la  puntuación  de  un  concursante  
a  las  puntuaciones  MutableMap.  Pasaremos  el  concursante  y  la  puntuación  
a  la  función  como  valores  de  parámetros;  siempre  que  la  puntuación  sea  0  o  
superior,  la  función  los  agregará  a  MutableMap  como  un  par  clave/valor.
Aquí  está  el  código  para  la  función:

clase  Concurso<T:  Mascota>  {

puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()

Concurso<T:  Mascota>

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {
puntuaciones

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)
añadirPuntuación
}
Poner  al  concursante  y  su  
puntuación  en  el  MutableMap,  
//Más  código  va  aquí
siempre  que  la  puntuación  
}
sea  mayor  o  igual  a  0.

Finalmente,  agreguemos  la  función  getWinners.

estas  aqui  4 297
Machine Translated by Google

función  obtenerGanadores

Mascotas

Concurso
Crear  la  función  obtenerGanadores minoristas
Veterinario
La  función  getWinners  debe  devolver  los  concursantes  con  la  puntuación  
más  alta.  Obtendremos  el  valor  de  la  puntuación  más  alta  de  la  propiedad  
de  puntuaciones  y  devolveremos  a  todos  los  concursantes  con  esta  
puntuación  en  un  MutableSet.  Como  cada  concursante  tiene  un  tipo  
genérico  de  T,  la  función  debe  tener  un  tipo  de  retorno  de  MutableSet<T>.

Aquí  está  el  código  para  la  función  getWinners:

Obtenga  el  valor  más  alto  de  las  puntuaciones.
diversión  getWinners():  MutableSet<T>  {

val  puntuación  más  alta  =  puntuaciones.valores.max() Concurso<T:  Mascota>

val  ganadores:  MutableSet<T>  =  mutableSetOf() puntuaciones

para  ((t,  puntaje)  en  puntajes)  {
añadirPuntuación
if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)
obtenerGanadores
} Agregue  cualquier  concursante  con  la  
ganadores  de  regreso puntuación  más  alta  a  un  MutableSet.

} Devuelve  el  MutableSet  de  ganadores.

Y  aquí  está  el  código  para  la  clase  Contest  completa:
Agregaremos  esta  clase  a  una  nueva  
clase  Concurso<T:  Mascota>  {
aplicación  unas  páginas  más  adelante.
puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)

diversión  getWinners():  MutableSet<T>  {

val  puntuación  más  alta  =  puntuaciones.valores.max()

val  ganadores:  MutableSet<T>  =  mutableSetOf()

para  ((t,  puntaje)  en  puntajes)  {

if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)

ganadores  de  regreso

Ahora  que  hemos  escrito  la  clase  Contest,  usémosla  para  crear  algunos  
objetos.

298  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Crear  algunos  objetos  de  concurso minoristas
Veterinario

Los  objetos  Contest  se  crean  especificando  con  qué  tipo  de  objetos  debe  tratar  y  
llamando  a  su  constructor.  El  siguiente  código,  por  ejemplo,  crea  un  objeto  Contest<Cat>  
llamado  catContest  que  se  ocupa  de  los  objetos  Cat:

val  catContest  =  Concurso<Cat>() Esto  crea  un  concurso  que  aceptará  gatos.

Esto  significa  que  puede  agregar  objetos  Cat  a  su  propiedad  de  puntajes  y  usar  su  
función  getWinners  para  devolver  un  MutableSet  of  Cats:

catContest.addScore(Gato("Fuzz  Lightyear"),  50)
getWinners()  devuelve  un  MutableSet<Cat>  porque  
concursogato.addScore(Gato("Katsu"),  45)
hemos  especificado  que  catContest  debe  tratar  
val  topCat  =  catContest.getWinners().first() con  Cats.

Y  como  Contest  usa  genéricos,  el  compilador  evita  que  le  pases  referencias  que  no  
sean  de  Cat.  El  siguiente  código,  por  ejemplo,  no  se  compilará:

El  compilador  le  impide  agregar  elementos  que  no  sean  
catContest.addScore(Perro("Fido"),  23)
gatos  a  Contest<Cat>,  por  lo  que  esta  línea  no  se  compilará.

Un  Contest<Pet>,  sin  embargo,  aceptará  cualquier  tipo  de  Mascota,  como  esta:

val  petContest  =  Concurso<Mascota>()
petContest.addScore(Gato("Fuzz  Lightyear"),  50) Dado  que  Contest<Pet>  trata  con  Mascotas,  los  

petContest.addScore(Pez("Finny  McGraw"),  56) concursantes  pueden  ser  cualquier  subtipo  de  Mascota.

El  compilador  puede  inferir  el  tipo  genérico.
En  algunas  circunstancias,  el  compilador  puede  inferir  el  tipo  genérico  de  la  
información  disponible.  Si,  por  ejemplo,  crea  una  variable  de  tipo  Contest<Dog>,  
el  compilador  inferirá  automáticamente  que  cualquier  objeto  Contest  que  le  pase  
es  un  Contest<Dog>  (a  menos  que  le  indique  lo  contrario).  El  siguiente  código,  por  
ejemplo,  crea  un  objeto  Contest<Dog>  y  lo  asigna  a  dogContest:

val  dogContest:  Concurso<Perro>
Aquí,  puede  usar  Contest()  en  lugar  de  Contest<Dog>()  ya  que  el  compilador  
dogContest  =  Concurso()
puede  inferir  el  tipo  de  objeto  del  tipo  de  variable.

Cuando  corresponda,  el  compilador  también  puede  inferir  el  tipo  genérico  a  partir  
de  los  parámetros  de  su  constructor.  Si,  por  ejemplo,  hubiéramos  usado  un  
parámetro  de  tipo  genérico  en  el  constructor  principal  de  la  clase  Contest  como  este:

clase  Concurso<T:  Mascota>(t:  T)  {...}

El  compilador  podría  inferir  que  el  siguiente  código  crea  un  Contest<Fish>: Esto  es  lo  mismo  que  crear  un  concurso  
usando  Contest<Fish>(Fish(“Finny  McGraw”)).
val  concurso  =  Concurso(Pez("Finny  McGraw")) Puede  omitir  <Fish>  ya  que  el  compilador  
lo  deduce  del  argumento  del  constructor.

estas  aqui  4 299
Machine Translated by Google

de  cerca

Funciones  genéricas  de  cerca

Hasta  ahora,  ha  visto  cómo  definir  una  función  que  usa  un  tipo  genérico  
dentro  de  una  definición  de  clase.  Pero,  ¿qué  sucede  si  desea  definir  una  
función  con  un  tipo  genérico  fuera  de  una  clase?  ¿O  qué  sucede  si  desea  
que  una  función  dentro  de  una  clase  use  un  tipo  genérico  que  no  está  incluido  
en  la  definición  de  la  clase?

Si  desea  definir  una  función  con  su  propio  tipo  genérico,  puede  hacerlo  
declarando  el  tipo  genérico  como  parte  de  la  definición  de  la  función.  El  
siguiente  código,  por  ejemplo,  define  una  función  denominada  listPet  con  
un  tipo  genérico,  T,  que  se  limita  a  los  tipos  de  Pet.  La  función  acepta  un  
parámetro  T  y  devuelve  una  referencia  a  un  objeto  MutableList<T>:

Para  las  funciones   diversión  <T:  Mascota>  listPet(t:  T):  MutableList<T>  {
que  declaran  su  propio  tipo   println("Crear  y  devolver  MutableList")
genérico,  la  <T:  Mascota>   volver  mutableListOf(t)
va  antes  del  nombre  de  la  función.
}

Tenga  en  cuenta  que  cuando  declara  una  función  genérica  de  esta  manera,  
el  tipo  debe  declararse  entre  corchetes  antes  del  nombre  de  la  función,  así:

divertido  <T:  Mascota>  listPet...

Para  llamar  a  la  función,  debe  especificar  el  tipo  de  objeto  con  el  que  
debe  tratar  la  función.  El  siguiente  código,  por  ejemplo,  llama  a  la  
función  listPet,  usando  paréntesis  angulares  para  especificar  que  la  estamos  
usando  con  objetos  Cat:

val  catList  =  listPet<Gato>(Gato("Zazzles"))

Sin  embargo,  el  tipo  genérico  puede  omitirse  si  el  compilador  puede  
inferirlo  de  los  argumentos  de  la  función.  El  siguiente  código,  por  ejemplo,   Estas  dos  llamadas  a  funciones  
es  legal  porque  el  compilador  puede  inferir  que  la  función  listPet  se  usa   hacen  lo  mismo,  ya  que  el  
con  Cats: compilador  puede  inferir  que  desea  
que  la  función  se  ocupe  de  Cats.
val  catList  =  listPet(Gato("Zazzles"))

300  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Crear  el  proyecto  Genéricos minoristas
Veterinario

Ahora  que  ha  visto  cómo  crear  una  clase  que  usa  genéricos,  agréguela  a  
una  nueva  aplicación.

Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asígnele  el  
nombre  "Genéricos".  Luego  cree  un  nuevo  archivo  de  Kotlin  llamado  Pets.kt  
resaltando  la  carpeta  src ,  haciendo  clic  en  el  menú  Archivo  y  eligiendo  Nuevo  
→  Archivo/Clase  de  Kotlin.  Cuando  se  le  solicite,  asigne  al  archivo  el  nombre  
de  "Mascotas"  y  elija  Archivo  en  la  opción  Tipo.

A  continuación,  actualice  su  versión  de  Pets.kt  para  que  coincida  con  la  nuestra  a  continuación:

clase  abstracta  Mascota  (var  nombre:  Cadena)

clase  Gato(nombre:  Cadena) :  Mascota(nombre)

Agregue  la  jerarquía  de  mascotas.

clase  Perro(nombre:  Cadena) :  Mascota(nombre)

clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)

Agregue  la  clase  Concurso.
clase  Concurso<T:  Mascota>  {

puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()
Genéricos

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {
origen

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)

}
Mascotas.kt

diversión  getWinners():  MutableSet<T>  {

val  ganadores:  MutableSet<T>  =  mutableSetOf()

val  puntuación  más  alta  =  puntuaciones.valores.max()

para  ((t,  puntaje)  en  puntajes)  {

if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)

ganadores  de  regreso

El  código  continúa  en  la  

página  siguiente.

estas  aqui  4 301
Machine Translated by Google

prueba  de  manejo

Mascotas

Concurso
El  código  continuó... minoristas
Veterinario
diversión  principal(argumentos:  Array<String>)  { Crea  dos  gatos  y  un  pez.
val  catFuzz  =  Gato("Fuzz  Lightyear")
val  catKatsu  =  Gato("Katsu")
val  fishFinny  =  Fish("Finny  McGraw")

Genéricos
val  catContest  =  Concurso<Cat>() Organice  un  concurso  de  gatos  (solo  para  gatos).

concursocat.addScore(catFuzz,  50)
origen
concursocat.addScore(catKatsu,  45)
val  topCat  =  catContest.getWinners().first()  println("El  ganador  del  
concurso  de  gatos  es  ${topCat.name}") Mascotas.kt

Realice  un  concurso  de  mascotas,  que  
val  petContest  =  Contest<Pet>()  
aceptará  todo  tipo  de  mascotas.
petContest.addScore(catFuzz,  50)  
petContest.addScore(fishFinny,  56)  val  topPet  =  
petContest.getWinners().first()  println("El  ganador  del  concurso  de  
mascotas  es  ${topPet.name}  ")
}

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  
salida  del  IDE:

El  ganador  del  concurso  de  gatos  es  Fuzz  Lightyear

El  ganador  del  concurso  de  mascotas  es  Finny  McGraw

Una  vez  que  haya  probado  el  siguiente  ejercicio,  veremos  la  jerarquía  de  
minoristas.

P:  ¿Puede  un  tipo  genérico  ser  anulable? P:  ¿Puede  una  clase  tener  más  de  un  tipo  genérico?

R:  Sí.  Si  tiene  una  función  que  devuelve  un  tipo  genérico  y R:  Sí.  Usted  define  múltiples  tipos  genéricos  especificándolos
desea  que  este  tipo  sea  anulable,  simplemente  agregue  un ?  después  del  tipo  de   dentro  de  paréntesis  angulares,  separados  por  una  coma.  Si  
retorno  genérico  como  este: quisiera  definir  una  clase  llamada  MyMap  con  tipos  genéricos  K  y  
V ,  la  definiría  usando  un  código  como  este:
clase  MiClase<T>  { fun  
miDiversión():  T? class  MiMapa<K,  V>  {
} //El  codigo  va  aqui
}

302  Capítulo  10
Machine Translated by Google

genéricos

Rompecabezas  de  piscina

Su  trabajo  es  tomar  fragmentos  de  código  del  
grupo  y  colocarlos  en  las  líneas  en  blanco  
del  código.  No  puede  usar  el  mismo  
fragmento  de  código  más  de  una  vez  y  no  
propietario  de  mascota  de  clase {

necesitará  usar  todos  los  fragmentos  de   val  mascotas  =  mutableListOf( )
código.  Su  objetivo  es  crear  una  clase  
llamada  PetOwner
agregar  diversión  ( ) {

que  acepta  tipos  genéricos  de  mascotas ,   mascotas.add( )
que  luego  debe  usar  para  crear  un  nuevo  
}
PetOwner<Cat>  que  contenga  referencias  a  dos  

objetos  Cat .
divertido  eliminar  ( ) {

mascotas.remove( )
mascotas  contiene  una  
}
referencia  a  cada  mascota   Dueño  de  mascota<T:  Mascota>

que  se  posee.  Se  inicializa   }
mascotas:  MutableList<T>
con  un  valor  que  se  pasa  
al  constructor  PetOwner. agregar  (t:   diversión  principal(argumentos:  Array<String>)  {
T)  eliminar  (t:  T)
val  catFuzz  =  Gato("Fuzz  Lightyear")

val  catKatsu  =  Gato("Katsu")

El  agregar  y  quitar Las  funciones  se   val  fishFinny  =  Fish("Finny  McGraw")

utilizan  para  actualizar  la  propiedad  de  mascotas. val  catOwner  =  PetOwner
La  función  add  agrega  una  referencia,   propietariogato.add(gatoKatsu)
y  el  quitar la  función  elimina  una.  
}

Nota:  cada  cosa  de  Nota:  cada  cosa  de  la  piscina  

solo  puede  ser  ¡ la  piscina  solo  se  puede  usar  una  

vez!  ¡usado  una  vez!

< < T T:  Mascota

> > T:  Mascota t:  T


T
t
( ( T:  Mascota
t:  T
gatofuzz
t
) ) pescadoFinny t:  T
t

estas  aqui  4 303
Machine Translated by Google

solución  de  rompecabezas  de  piscina

Solución  de  rompecabezas  de  piscina
Su  trabajo  es  tomar  fragmentos  de  código  del  
Especifique  el  tipo  genérico. el  constructor
grupo  y  colocarlos  en
las  líneas  en  blanco  en  el  código.  Tú
no  puede  usar  el  mismo  fragmento   T)  clase  PetOwner <T:  Mascota>(t:   {

de  código  más  de  una  vez,  y  no   val  mascotas  =  mutableListOf( ) t
necesitará  usar  todos  los  fragmentos  
de  código.  Su  objetivo  es  crear  una   Esto  crea  una  
clase  llamada  PetOwner agregar  diversión  ( t:  T ) {
MutableList<T>.
que  acepta  tipos  genéricos  de  mascotas ,   mascotas.add( ) t
que  luego  debe  usar  para  crear  un  nuevo  
} Añadir/Quitar  valores  T.
PetOwner<Cat>  que  contiene

referencias  a  dos  objetos  Cat .
divertido  eliminar  ( t:  t ) {

mascotas.remove( ) t

}
Dueño  de  mascota<T:  Mascota>
}
mascotas:  MutableList<T>

agregar  (t:   diversión  principal(argumentos:  Array<String>)  {
T)  eliminar  (t:  T)
val  catFuzz  =  Gato("Fuzz  Lightyear")

val  catKatsu  =  Gato("Katsu")

val  fishFinny  =  Fish("Finny  McGraw")

val  catOwner  =  PetOwner (gato  Fuzz)

propietariogato.add(gatoKatsu)

}
Crea  un  PetOwner<Cat>  e  
inicializa  las  mascotas  con  
una  referencia  a  catFuzz.

No  necesitabas  
usar  estos  fragmentos. T

< T

> T:  Mascota
T
T:  Mascota

pescadoFinny

304  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
La  jerarquía  del  minorista minoristas
Veterinario

Vamos  a  usar  las  clases  de  mascotas  que  creamos  
anteriormente  para  definir  una  jerarquía  de  minoristas  que  
pueden  vender  diferentes  tipos  de  mascotas.  Para  hacer  esto,  
definiremos  una  interfaz  Retailer  con  una  función  de  venta  y  tres  
clases  concretas  llamadas  CatRetailer,  DogRetailer  y  FishRetailer  
que  implementan  la  interfaz.

Cada  tipo  de  minorista  debe  poder  vender  un  tipo  particular  
P:  ¿ Por  qué  no  utiliza  un  PetRetailer ?
de  objeto.  Un  Minorista  de  Gatos,  por  ejemplo,  solo  puede  
clase  concreta?
vender  Gatos,  y  un  Minorista  de  Perros  solo  puede  vender  
Perros.  Para  hacer  cumplir  esto,  usaremos  genéricos  para  
especificar  el  tipo  de  objeto  con  el  que  trata  cada  clase.   R:  En  el  mundo  real,  es  bastante  probable  que
desea  incluir  un  PetRetailer  que  vende  todos
Agregaremos  un  tipo  genérico  T  a  la  interfaz  del  minorista  y  
tipos  de  mascota.  Aquí,  estamos  diferenciando  entre  
especificaremos  que  la  función  de  venta  debe  devolver  objetos  de  
los  diferentes  tipos  de  minoristas  para  que  podamos  
este  tipo.  Como  las  clases  CatRetailer,  DogRetailer  y  FishRetailer  
enseñarle  detalles  más  importantes  sobre  los  genéricos.
implementan  esta  interfaz,  cada  una  tendrá  que  sustituir  el  tipo  de  
objeto  “real”  con  el  que  tratan  por  el  tipo  genérico  T.

Esta  es  la  jerarquía  de  clases  que  usaremos:

(interfaz)
Minorista<T>

Retailer  es  una  interfaz,  mientras  
vender():  T que  CatRetailer,  DogRetailer  y  
FishRetailer  son  implementaciones  
concretas.

CatRetailer PerroMinorista PescadoMinorista

vender  ():  gato vender  ():  perro vender  ():  pescado

Ahora  que  ha  visto  la  jerarquía  de  clases,  escribamos  el  código,  
comenzando  con  la  interfaz  del  minorista.

estas  aqui  4 305
Machine Translated by Google

Interfaz  de  minorista
Mascotas

Concurso
Definir  la  interfaz  del  minorista minoristas
Veterinario

La  interfaz  del  minorista  debe  especificar  que  utiliza  un  tipo  T  genérico,  que  se  utiliza  
como  tipo  de  devolución  para  la  función  de  venta.

Aquí  está  el  código  para  la  interfaz:

interfaz  Minorista<T>  { (interfaz)
venta  divertida():  T Minorista<T>
}

Las  clases  CatRetailer,  DogRetailer  y  FishRetailer  necesitan  implementar  la  interfaz  
vender():  T
Retailer,  especificando  el  tipo  de  objeto  que  trata  cada  una.  La  clase  CatRetailer,  por  
ejemplo,  solo  trata  con  Cats,  por  lo  que  la  definiremos  usando  un  código  como  este:

clase  CatRetailer :  Minorista<Cat>  { CatRetailer
anular  venta  divertida():  Gato  {
println("Vender  gato")
La  clase  CatRetailer  implementa  la  
interfaz  Retailer  para  que  trate  con  Cats.   vender  ():  gato
devolver  gato("")
Esto  significa  que  la  función  vender()  
}
debe  devolver  un  Cat.
}

De  manera  similar,  la  clase  DogRetailer  se  ocupa  de  los  perros,  por  lo  que  
podemos  definirla  así:
(interfaz)
clase  DogRetailer :  Minorista<Perro>  { Minorista<T>

anular  venta  divertida():  Perro  {
DogRetailer  reemplaza  
println("Vender  perro")
el  tipo  genérico  de  Retailer  con   vender():  T
volver  Perro("")
Dog,  por  lo  que  su  función  sell()  
}
debe  devolver  un  Dog.
}
Cada  implementación  de  la  interfaz  Retailer  debe  especificar  el  tipo  de  objeto  que  
trata  reemplazando  la  “T”  definida  en  la  interfaz  por  el  tipo  real.  La  implementación   PerroMinorista

de  CatRetailer,  por  ejemplo,  reemplaza  "T"  con  "Cat",  por  lo  que  su  función  de  venta  
debe  devolver  un  Cat.  Si  intenta  usar  algo  que  no  sea  Cat  (o  un  subtipo  de  Cat)  para  
el  tipo  de  retorno  de  venta,  el  código  no  se  compilará: vender  ():  perro

clase  CatRetailer :  Minorista<Cat>  { Este  código  no  se  compilará  porque  
anular  la  venta  divertida  ():  Perro  =  Perro  ("") la  función  sell()  de  CatRetailer  debe  devolver  
} un  Gato,  y  un  Perro  no  es  un  tipo  de  Gato.

Por  lo  tanto,  usar  genéricos  significa  que  puede  establecer  límites  sobre  cómo  una  clase  
usa  sus  tipos,  lo  que  hace  que  su  código  sea  mucho  más  consistente  y  robusto.

Ahora  que  tenemos  el  código  para  nuestros  minoristas,  creemos  algunos  objetos.

306  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Podemos  crear  objetos   minoristas

CatRetailer,  DogRetailer  y  FishRetailer... Veterinario

Como  era  de  esperar,  puede  crear  un  objeto  CatRetailer,  
DogRetailer  o  FishRetailer  y  asignarlo  a  una  variable  declarando  
explícitamente  el  tipo  de  variable  o  dejando  que  el  compilador  lo  
deduzca  del  valor  que  se  le  asigna.  El  siguiente  código  utiliza  estas  
técnicas  para  crear  dos  variables  CatRetailer  y  asignar  un  objeto  
CatRetailer  a  cada  una:

val  catRetailer1  =  CatRetailer()

val  minoristaCat2:  MinoristaCat  =  MinoristaCat()

...  pero  ¿qué  pasa  con  el  polimorfismo?
Como  CatRetailer,  DogRetailer  y  FishRetailer  implementan  la  
interfaz  Retailer,  deberíamos  poder  crear  una  variable  de  tipo  
Retailer  (con  un  parámetro  de  tipo  compatible)  y  asignarle  uno  de  
sus  subtipos.  Y  esto  funciona  si  asignamos  un  objeto  CatRetailer  a  
una  variable  Retailer<Cat>,  o  asignamos  un  DogRetailer  a  un  
Retailer<Dog>:
Estas  líneas  son  legales  porque  
val  perroMinorista:  Minorista<Perro>  =  PerroMinorista()
DogRetailer  implementa  Retailer<Dog>  
val  catRetailer:  Minorista<Cat>  =  CatRetailer() y  CatRetailer  implementa  Retailer<Cat>.

Pero  si  intentamos  asignar  uno  de  estos  objetos  a  Retailer<Pet>,  el  
código  no  se  compilará:
Esto  no  se  compilará,  aunque  
val  petRetailer:  Minorista<Mascota>  =  CatRetailer() CatRetailer  es  un  Retailer<Cat>  y  
Cat  es  un  subtipo  de  Pet.
Aunque  CatRetailer  es  un  tipo  de  Retailer,  y  Cat  es  un  tipo  
de  Pet,  nuestro  código  actual  no  nos  permitirá  asignar  un  
objeto  Retailer<Cat>  a  una  variable  Retailer<Pet>.  Una  variable  
Retailer<Pet>  solo  aceptará  un  objeto  Retailer<Pet>.  No  un  
Minorista<Gato>,  ni  un  Minorista<Perro>,  sino  solo  un  
Minorista<Mascota>.

Este  comportamiento  parece  violar  todo  el  punto  del  
polimorfismo.  La  buena  noticia,  sin  embargo,  es  que  podemos  
ajustar  el  tipo  genérico  en  la  interfaz  de  Retailer  para  controlar  qué  
tipos  de  objetos  puede  aceptar  una  variable  Retailer<Pet>.

estas  aqui  4 307
Machine Translated by Google

covarianza

Mascotas

Concurso
Use  out  para  hacer  una  covariante  de  tipo  genérico minoristas
Veterinario
Si  desea  poder  utilizar  un  objeto  de  subtipo  genérico  en  lugar  de  un  
supertipo  genérico,  puede  hacerlo  prefijando  el  tipo  genérico  sin .  En  
nuestro  ejemplo,  queremos  poder  asignar  un  Minorista<Gato>  (un  subtipo)  
a  un  Minorista<Mascota>  (un  supertipo),  por  lo  que  agregaremos  el  prefijo  
del  tipo  genérico  T  en  la  interfaz  del  Minorista  sin  así:
Si  un  tipo  genérico  
es  covariante,  significa  
interfaz  Minorista<out  T>  {

venta  divertida():  T
Aquí  está  el  prefijo  de  salida. que  puede  usar  un  subtipo  
}
en  lugar  de  un  supertipo.
Cuando  anteponemos  un  tipo  genérico  con  out,  decimos  que  el  tipo  genérico  
es  covariante.  En  otras  palabras,  significa  que  se  puede  usar  un  subtipo  en  
lugar  de  un  supertipo.

Hacer  el  cambio  anterior  significa  que  a  una  variable  Retailer<Pet>  
ahora  se  le  pueden  asignar  objetos  Retailer  que  se  ocupan  de  los  subtipos  
de  Mascotas.  El  siguiente  código,  por  ejemplo,  ahora  compila: El  prefijo  out  en  la  interfaz  de  Retailer  
significa  que  ahora  podemos  asignar  un  
val  petRetailer:  Minorista<Mascota>  =  CatRetailer()
Retailer<Cat>  a  una  variable  
Retailer<Pet>.
En  general,  un  tipo  genérico  de  clase  o  interfaz  puede  tener  el  prefijo  out  si  
la  clase  tiene  funciones  que  lo  usan  como  un  tipo  de  devolución,  o  si  la  clase  
tiene  propiedades  val  de  ese  tipo.  Sin  embargo,  no  puede  usar  si  la  clase  
tiene  parámetros  de  función  o  propiedades  var  de  ese  tipo  genérico.
Otra  forma  de  pensar  en  esto  es  que  un  tipo  genérico  
que  tiene  el  prefijo  out  solo  se  puede  usar  en  una  posición  
"out",  como  un  tipo  de  retorno  de  función.  Sin  embargo,  
Las  colecciones  se  definen  utilizando  tipos  covariantes. no  se  puede  usar  en  una  posición  "adentro",  por  lo  que  
una  función  no  puede  recibir  un  tipo  covariante  como  valor  
El  prefijo  out  no  solo  lo  usan  las  clases  e  interfaces  genéricas  que  usted  
de  parámetro.
mismo  define.  También  son  muy  utilizados  por  el  código  integrado  de  Kotlin,  
como  las  colecciones.

La  colección  List,  por  ejemplo,  se  define  usando  un  código  como  este:

interfaz  pública  List<out  E> ...  { ... }

Esto  significa  que  puede,  por  ejemplo,  asignar  una  Lista  de  gatos  a  una  
Lista  de  mascotas,  y  el  código  se  compilará:

val  catList:  Lista<Gato>  =  listaDe(Gato(""),  Gato(""))

val  petList:  List<Pet>  =  catList

Ahora  que  ha  visto  cómo  hacer  que  los  tipos  genéricos  sean  covariantes,  
agreguemos  el  código  que  hemos  escrito  a  nuestro  proyecto.

308  Capítulo  10
Machine Translated by Google

genéricos

Mascotas

Concurso
Actualizar  el  proyecto  Generics minoristas
Veterinario

Actualice  su  versión  de  Pets.kt  en  el  proyecto  Generics  para  que  coincida  con  la  
nuestra  a  continuación  (nuestros  cambios  están  en  negrita):

clase  abstracta  Mascota  (var  nombre:  Cadena)

clase  Gato(nombre:  Cadena) :  Mascota(nombre)
Genéricos
clase  Perro(nombre:  Cadena) :  Mascota(nombre)

clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)
origen

clase  Concurso<T:  Mascota>  {
Mascotas.kt
puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)
}

diversión  getWinners():  MutableSet<T>  {
val  ganadores:  MutableSet<T>  =  mutableSetOf()

val  puntuación  más  alta  =  puntuaciones.valores.max()

para  ((t,  puntaje)  en  puntajes)  {

if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)
}
ganadores  de  regreso

}
} Agregue  la  interfaz  de  minorista.

interfaz  Minorista<out  T>  {

venta  divertida():  T

clase  CatRetailer :  Minorista<Cat>  {

anular  venta  divertida():  Gato  {

println("Vender  gato")  return  Gato("")

Agregue  las  clases  
}
CatRetailer  y  DogRetailer.
}

class  DogRetailer :  Retailer<Dog>  { override  fun  sell():  Dog  

{ println("Vender  perro")  return  Dog("")

El  código  continúa  en  

} la  página  siguiente.

}
estas  aqui  4 309
Machine Translated by Google

prueba  de  manejo

Mascotas

Concurso
El  código  continuó... minoristas
Veterinario
clase  MinoristaPescado :  Minorista<Pescado>  {
Agregue  la  clase  FishRetailer.
anular  la  venta  divertida  ():  Pescado  {
println("Vender  pescado")  return  
Pescado("")
}
}

diversión  principal(argumentos:  Array<String>)  { Genéricos

val  catFuzz  =  Gato("Fuzz  Lightyear")  val  catKatsu  =  
Gato("Katsu")
origen

val  fishFinny  =  Fish("Finny  McGraw")

Mascotas.kt
val  catContest  =  Concurso<Cat>()
concursocat.addScore(catFuzz,  50)
concursocat.addScore(catKatsu,  45)
val  topCat  =  catContest.getWinners().first()  println("El  ganador  del  concurso  
de  gatos  es  ${topCat.name}")

val  petContest  =  Concurso<Mascota>()  
petContest.addScore(catFuzz,  50)
petContest.addScore(fishFinny,  56)
val  topPet  =  petContest.getWinners().first()  println("El  ganador  del  concurso  
de  mascotas  es  ${topPet.name}")

Cree  algunos  objetos  Minorista.
val  perroMinorista:  Minorista<Perro>  =  PerroMinorista()  val  gatoMinorista:  
Minorista<Gato>  =  GatoMinorista()
val  petRetailer:  Retailer<Pet>  =  CatRetailer()  petRetailer.sell()

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  
salida  del  IDE:

El  ganador  del  concurso  de  gatos  es  Fuzz  Lightyear

El  ganador  del  concurso  de  mascotas  es  Finny  McGraw

vender  gato

Ahora  que  ha  visto  cómo  hacer  que  los  tipos  genéricos  sean  covariantes  
usando  el  prefijo  out,  pruebe  el  siguiente  ejercicio.

310  Capítulo  10
Machine Translated by Google

genéricos

SER  el  compilador
Aquí  hay  cinco  clases  e  
interfaces  que  usan  genéricos.
Tu  trabajo  es  jugar  como  si  
fueras  el  
Compilador  y  
determinar  si  cada  
uno  compilará.  Si  
no  se  compila,  ¿por  qué  no?

interfaz  A<salida  T>  {
A
diversión  myFunction(t:  T)

B interfaz  B<salida  T>  {
valor  x:  T

diversión  myFunction():  T

C interfaz  C<salida  T>  {

variar:  T

diversión  myFunction():  T

D interfaz  D<salida  T>  {

fun  myFunction(str:  String):  T

clase  abstracta  E<out  T>(t:  T)  {
mi
valor  x  =  t

estas  aqui  4 311
Machine Translated by Google

ser  la  solución  del  compilador

SER  la  solución  del  compilador
Aquí  hay  cinco  clases  e  
interfaces  que  usan  genéricos.
Tu  trabajo  es  jugar  como  si  
fueras  el  
Compilador  y  
determinar  si  cada  
uno  compilará.  Si  
no  se  compila,  ¿por  qué  no?

A interfaz  A<salida  T>  { Este  código  no  se  compilará  porque  el  tipo  covariante  T  no  se  
diversión  myFunction(t:  T) puede  usar  como  un  parámetro  de  función.
}

B interfaz  B<salida  T>  { Este  código  se  compila  correctamente.

valor  x:  T

diversión  myFunction():  T

C interfaz  C<salida  T>  { Este  código  no  se  compilará  porque  el  tipo  covariante  T  no  se  

variar:  T puede  usar  como  el  tipo  de  una  propiedad  var.

diversión  myFunction():  T

D interfaz  D<salida  T>  { Este  código  se  compila  correctamente.

fun  myFunction(str:  String):  T

mi clase  abstracta  E<out  T>(t:  T)  { Este  código  se  compila  correctamente.
valor  x  =  t

312  Capítulo  10
Machine Translated by Google

genéricos

Mascotas

Concurso
Necesitamos  una  clase  de  veterinario
minoristas
Veterinario

Como  dijimos  anteriormente  en  el  capítulo,  queremos  poder  asignar  un  
veterinario  a  cada  concurso  en  caso  de  que  haya  una  emergencia  médica  con  
alguno  de  los  concursantes.  Como  los  veterinarios  pueden  especializarse  en  el  
tratamiento  de  diferentes  tipos  de  mascotas,  crearemos  una  clase  Vet  con  un  tipo  
T  genérico  y  especificaremos  que  tiene  una  función  de  tratamiento  que  acepta  un  
argumento  de  este  tipo.  También  diremos  que  T  debe  ser  un  tipo  de  Mascota  para  
que  no  puedas  crear  un  Veterinario  que  trate,  digamos,  objetos  Planeta  o  Brócoli.

Aquí  está  la  clase  de  veterinario

clase  Veterinario<T:  Mascota>  {

regalo  divertido  (t:  T)  { Veterinario<T:  Mascota>

println("Tratar  mascota  ${t.nombre}")

}
tratar  (t:  T)
}

A  continuación,  cambiemos  la  clase  de  concurso  para  que  acepte  un  veterinario.

Asignar  un  veterinario  a  un  concurso

Queremos  asegurarnos  de  que  cada  Concurso  tenga  un  Vet,  por  lo  que  
Estamos  agregando  un  Vet<T>  al  
agregaremos  una  propiedad  Vet  al  constructor  del  Concurso.  Aquí  está  el  código  
constructor  del  Concurso  para  que  no  
del  Concurso  actualizado:
pueda  crear  un  Concurso  sin  asignarle  un  Vet.
clase  Concurso<T:  Mascota>(var  vet:  Vet<T>)  {

puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)

diversión  getWinners():  MutableSet<T>  {

val  ganadores:  MutableSet<T>  =  mutableSetOf()

val  puntuación  más  alta  =  puntuaciones.valores.max()

para  ((t,  puntaje)  en  puntajes)  {

if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)

}
ganadores  de  regreso

Vamos  a  crear  algunos  objetos  Vet  y  asignarlos  a  Concursos.

estas  aqui  4 313
Machine Translated by Google

crear  algunos  veterinarios

Mascotas

Concurso
Crear  objetos  veterinarios minoristas
Veterinario
Podemos  crear  objetos  Vet  de  la  misma  manera  que  creamos  
objetos  de  Concurso:  especificando  el  tipo  de  objeto  con  el  que  debe  
tratar  cada  objeto  Vet.  El  siguiente  código,  por  ejemplo,  crea  tres  objetos,  
uno  de  tipo  Vet<Cat>,  Vet<Fish>  y  Vet<Pet>:

val  catVet  =  Veterinario<Gato>()

val  fishVet  =  Vet<Pez>()

val  petVet  =  Veterinario<Mascota>()

Cada  veterinario  puede  tratar  con  un  tipo  específico  de  mascota.  
Vet<Cat>,  por  ejemplo,  puede  tratar  gatos,  mientras  que  Vet<Pet>  puede  
tratar  a  cualquier  mascota,  incluidos  gatos  y  peces.  Un  Vet<Cat>,  sin  
embargo,  no  puede  tratar  nada  que  no  sea  un  Gato,  como  un  Pez:
Tanto  un  Vet<Cat>  como  un  Vet<Pet>  pueden  tratar  Gatos.
catVet.treat(Gato("Fuzz  Lightyear"))

petVet.treat(Gato("Katsu"))
Un  Vet<Pet>  puede  tratar  a  un  Pez.
petVet.treat(Pez("Finny  McGraw"))

catVet.treat(Pescado("Finny  McGraw"))
Esta  línea  no  se  compilará,  ya  que  un  Vet<Cat>  no  puede  tratar  a  un  Pez.

Veamos  qué  sucede  cuando  intentamos  pasar  objetos  Vet  a
Concursos.

Pase  un  veterinario  al  constructor  del  concurso

La  clase  Concurso  tiene  un  parámetro,  un  Veterinario,  que  debe  
ser  capaz  de  tratar  el  tipo  de  Mascota  para  el  que  es  el  Concurso.  
Esto  significa  que  podemos  pasar  un  Vet<Cat>  a  un  Contest<Cat>,  y  un  
Vet<Pet>  a  un  Contest<Pet>  así:

val  catContest  =  Concurso<Cat>(catVet)

val  petContest  =  Concurso<Mascota>(petVet)

Pero  hay  un  problema.  Un  Vet<Pet>  puede  tratar  todos  los  tipos  de  
mascotas,  incluidos  los  gatos,  pero  no  podemos  asignar  un  Vet<Pet>  a  
un  Contest<Cat>  porque  el  código  no  se  compilará:
Aunque  un  Vet<Pet>  puede  tratar  gatos,  un  
val  catContest  =  Concurso<Gato>(petVet)
Contest<Cat>  no  aceptará  un  Vet<Pet>,  por  lo  
que  esta  línea  no  se  compilará.
Entonces,  ¿qué  debemos  hacer  en  esta  situación?

314  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
Usar  in  para  hacer  un  tipo  genérico  contravariante minoristas
Veterinario

En  nuestro  ejemplo,  queremos  poder  pasar  Pet<Vet>  a  Contest<Cat>  
en  lugar  de  Pet<Cat>.  En  otras  palabras,  queremos  poder  usar  un  supertipo  
genérico  en  lugar  de  un  subtipo  genérico.

En  esta  situación,  podemos  resolver  el  problema  prefijando  el  tipo  genérico   Si  un  tipo  genérico  
usado  por  la  clase  Vet  con  in.  in  es  el  polo  opuesto  de  out.
Mientras  que  out  le  permite  usar  un  subtipo  genérico  en  lugar  de  un  supertipo  
es  contravariante,
(como  asignar  un  Retailer<Cat>  a  un  Retailer<Pet>),  in  le  permite  usar  un  
supertipo  genérico  en  lugar  de  un  subtipo.  Así  que  prefijando  el  tipo  genérico   significa  que  puede  
de  la  clase  Vet  con  esto:

Aquí  está  el  prefijo  in.
usar  un  supertipo  en  
class  Veterinario<en  T:  Mascota>  {
lugar  de  un  subtipo.
regalo  divertido  (t:  T)  {

println("Tratar  mascota  ${t.nombre}") Esto  es  lo  opuesto  a  
} la  covarianza.
}

significa  que  podemos  usar  un  Vet<Pet>  en  lugar  de  un  Vet<Cat>.  Ahora  se  
compila  el  siguiente  código: El  prefijo  in  en  la  clase  Vet  significa  
que  ahora  podemos  usar  Vet<Pet>  
val  catContest  =  Concurso<Gato>(Veterinario<Mascota>())
en  lugar  de  Vet<Cat>,  por  lo  que  este  
código  ahora  se  compila.
Cuando  anteponemos  un  tipo  genérico  con  in,  decimos  que  el  tipo  genérico  
es  contravariante.  En  otras  palabras,  significa  que  se  puede  usar  un  supertipo  
en  lugar  de  un  subtipo.

En  general,  un  tipo  genérico  de  clase  o  interfaz  puede  tener  el  prefijo  in  si  la  
En  otras  palabras,  un  tipo  genérico  
clase  tiene  funciones  que  lo  usan  como  un  tipo  de  parámetro.  Sin  embargo,  
que  tiene  el  prefijo  "in"  solo  se  puede  
no  puede  usar  in  si  alguna  de  las  funciones  de  clase  lo  usa  como  un  tipo  de   usar  en  una  posición  "in",  como  un  
devolución,  o  si  ese  tipo  lo  usa  alguna  propiedad,  independientemente  de  si   valor  de  parámetro  de  función.  No  se  
están  definidas  usando  val  o  var. puede  utilizar  en  posiciones  de  “fuera”.

¿Debe  un  Vet<Cat>  SIEMPRE  aceptar  un  Vet<Pet>?
Antes  de  anteponer  in  a  un  tipo  genérico  de  clase  o  interfaz,  debe  
considerar  si  desea  que  el  subtipo  genérico  acepte  un  supertipo  genérico  
en  cada  situación.  in  le  permite,  por  ejemplo,  asignar  un  objeto  Vet<Pet>  a  
la  variable  Vet<Cat>,  lo  que  puede  no  ser  algo  que  siempre  quiera  que  
suceda:

val  catVet:  Veterinario<Gato>  =  Veterinario<Mascota>()
Esta  línea  se  compila  a  medida  que  la  clase  Vet  usa  un  prefijo  in  para  T.

La  buena  noticia  es  que,  en  situaciones  como  esta,  puede  adaptar  las  
circunstancias  en  las  que  un  tipo  genérico  es  contravariante.  Veamos  cómo.

estas  aqui  4 315
Machine Translated by Google

contravarianza  local

Mascotas

Concurso
Un  tipo  genérico  puede  ser  localmente  contravariante minoristas
Veterinario
Como  ha  visto,  anteponer  un  tipo  genérico  con  in  como  parte  de  la  
declaración  de  clase  o  interfaz  hace  que  el  tipo  genérico  sea  
contravariante  globalmente.  Sin  embargo,  puede  restringir  este  
comportamiento  a  propiedades  o  funciones  específicas.
Cuando  un  tipo  genérico  
Supongamos,  por  ejemplo,  que  queremos  poder  usar  una   no  tiene  prefijo  de  entrada  o  
referencia  Vet<Pet>  en  lugar  de  Vet<Cat>,  pero  solo  donde  se  
pasa  a  Contest<Cat>  en  su  constructor.  Podemos  lograr  esto   salida,  decimos  que  el  tipo
eliminando  el  prefijo  in  del  tipo  genérico  en  la  clase  Vet  y  agregándolo   es  invariante.  Un  
a  la  propiedad  vet  en  el  constructor  Contest.

tipo  invariable  solo  
Aquí  está  el  código  para  hacer  esto:
Eliminar  el  prefijo  in  
de  la  clase  Vet...
puede  aceptar  referencias  
class  Veterinario<en  T:  Mascota>  {
de  ese  tipo  específico.
regalo  divertido  (t:  T)  {

println("Tratar  mascota  ${t.nombre}")

clase  Concurso<T:  Mascota>(var  vet:  Veterinario<in  T>)  {
...
...  y  añádelo  al  constructor  Contest  en  
} su  lugar.  Esto  significa  que  T  es  
contravariante,  pero  solo  en  el  constructor  Contest.
Estos  cambios  significan  que  aún  puede  pasar  un  Vet<Pet>  a  un
Concurso<Cat>  así:
Esta  línea  se  compila,  ya  que  puede  
val  catContest  =  Concurso<Gato>(Veterinario<Mascota>()) usar  Vet<Pet>  en  lugar  de  Vet<Cat>  en  
el  constructor  Contest<Cat>.
Sin  embargo,  el  compilador  no  le  permitirá  asignar  un  objeto  
Vet<Pet>  a  una  variable  Vet<Cat>  ya  que  el  tipo  genérico  de  Vet  no  
es  globalmente  contravariable:
Esta  línea,  sin  embargo,  no  se  compilará  ya  que  no  
val  catVet:  Veterinario<Gato>  =  Veterinario<Mascota>() puede  usar  globalmente  Vet<Pet>  en  lugar  de  Vet<Cat>.

Ahora  que  ha  aprendido  a  usar  la  contravarianza,  agreguemos  el  código  
Vet  a  nuestro  proyecto  Genéricos.

316  Capítulo  10
Machine Translated by Google

genéricos

Mascotas

Concurso
Actualizar  el  proyecto  Generics minoristas
Veterinario

Actualice  su  versión  de  Pets.kt  en  el  proyecto  Generics  para  que  coincida  con  la  
nuestra  a  continuación  (nuestros  cambios  están  en  negrita):

clase  abstracta  Mascota  (var  nombre:  Cadena)

clase  Gato(nombre:  Cadena) :  Mascota(nombre)
Genéricos
clase  Perro(nombre:  Cadena) :  Mascota(nombre)

clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)
origen

Agregue  la  clase  Vet.
clase  Veterinario<T:  Mascota>  {

regalo  divertido  (t:  T)  { Mascotas.kt

println("Tratar  mascota  ${t.nombre}")

}
Agrega  un  constructor  a  la  clase  Contest.

clase  Concurso<T:  Mascota>(var  vet:  Veterinario<in  T>)  {

puntuaciones  val:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)
}

diversión  getWinners():  MutableSet<T>  {
val  ganadores:  MutableSet<T>  =  mutableSetOf()

val  puntuación  más  alta  =  puntuaciones.valores.max()

para  ((t,  puntaje)  en  puntajes)  {

if  (puntuación  ==  puntuación  más  alta)  ganadores.add(t)
}
ganadores  de  regreso

}
}

interfaz  Minorista<out  T>  {

venta  divertida():  T

clase  CatRetailer :  Minorista<Cat>  {

anular  venta  divertida():  Gato  {

println("Vender  gato")
devolver  gato("")

} El  código  continúa  en  
} la  página  siguiente.

estas  aqui  4 317
Machine Translated by Google

más  código

Mascotas

Concurso
El  código  continuó... minoristas
Veterinario

clase  DogRetailer :  Minorista<Perro>  {

anular  venta  divertida():  Perro  {

println("Vender  perro")

volver  Perro("")

clase  MinoristaPescado :  Minorista<Pescado>  {

anular  la  venta  divertida  ():  Pescado  {

println("Vender  pescado")
Genéricos
devolver  pescado("")

}
origen
}

Mascotas.kt
diversión  principal(argumentos:  Array<String>)  {
val  catFuzz  =  Gato("Fuzz  Lightyear")

val  catKatsu  =  Gato("Katsu")

val  fishFinny  =  Fish("Finny  McGraw")

Crea  algunos  objetos  veterinarios.
val  catVet  =  Veterinario<Gato>()

val  fishVet  =  Vet<Pez>()

val  petVet  =  Veterinario<Mascota>()

Haz  que  los  veterinarios  traten  a  algunas  mascotas.

gatoVet.treat(gatoFuzz)

petVet.treat(gatoKatsu)

petVet.treat(fishFinny)
Asigne  un  Veterinario<Gato>  al  Concurso<Gato>.

val  catContest  =  Concurso<Cat>(catVet)

concursocat.addScore(catFuzz,  50)

concursocat.addScore(catKatsu,  45)

val  topCat  =  catContest.getWinners().first()

println("El  ganador  del  concurso  de  gatos  es  ${topCat.name}")

El  código  continúa  en  la  

página  siguiente.

318  Capítulo  10
Machine Translated by Google

genéricos
Mascotas

Concurso
El  código  continuó... Asigne  un  Vet<Pet>  al  Contest<Pet>.
minoristas
Veterinario
val  petContest  =  Concurso<Mascota>(petVet)
petContest.addScore(catFuzz,  50)
petContest.addScore(fishFinny,  56)
val  topPet  =  petContest.getWinners().first()
Genéricos
println("El  ganador  del  concurso  de  mascotas  es  ${topPet.name}")

Asigne  un  Vet<Pet>   origen
val  fishContest  =  Concurso<Pescado>(petVet)
a  un  Contest<Fish>.

Mascotas.kt
val  perroMinorista:  Minorista<Perro>  =  PerroMinorista()
val  catRetailer:  Minorista<Cat>  =  CatRetailer()

val  petRetailer:  Minorista<Mascota>  =  CatRetailer()
petRetailer.vender()
}

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  
salida  del  IDE:

Tratar  Pet  Fuzz  Lightyear
Tratar  a  la  mascota  Katsu

Tratar  a  la  mascota  Finny  McGraw
El  ganador  del  concurso  de  gatos  es  Fuzz  Lightyear

El  ganador  del  concurso  de  mascotas  es  Finny  McGraw
vender  gato

P:  ¿No  podría  haber  convertido  la  propiedad  veterinaria  de  Contest  en  una P:  El  enfoque  de  Kotlin  para  los  genéricos  parece  diferente  al  de  Java.
¿Veterinario  <Mascota>? ¿Está  bien?

R:  No.  Esto  significaría  que  la  propiedad  veterinaria  solo  podría R:  Sí,  lo  es.  Con  Java,  los  tipos  genéricos  siempre  son  invariables,  pero
aceptar  un  Vet<Pet>.  Y  aunque  podría  hacer  que  la  propiedad  vet   puede  usar  comodines  para  sortear  algunos  de  los  problemas  que  esto  

sea  localmente  covariante  usando: crea.  Kotlin,  sin  embargo,  le  brinda  un  control  mucho  mayor,  ya  que  puede  
hacer  que  los  tipos  genéricos  sean  covariantes,  contravariantes  o  dejarlos  
como  invariantes.
var  vet:  Veterinario<fuera  Mascota>

significaría  que  podrías  asignar  un  Veterinario<Pez>  a  un  
Concurso<Gato>,  lo  cual  es  poco  probable  que  termine  bien.
estas  aqui  4 319
Machine Translated by Google

ser  el  compilador

SER  el  compilador
Aquí  hay  cuatro  clases  e  interfaces  

que  usan  genéricos.
Tu  trabajo  es  jugar  como  si  fueras  el  
Compilador  y  determinar  
si  cada  uno  compilará.  Si  
no  se  compila,  ¿por  qué  

no?

A clase  A<en  T>(t:  T)  {

fun  myFunction(t:  T)  { }

B clase  B<en  T>(t:  T)  {
valor  x  =  t

fun  myFunction(t:  T)  { }

C clase  abstracta  C<en  T>  {

fun  myFunction():  T  { }

D clase  E<en  T>(t:  T)  {

var  y  =  t

fun  myFunction(t:  T)  { }

Respuestas  en  la  página  322.

320  Capítulo  10
Machine Translated by Google

genéricos

A  continuación  se  muestra  una  lista  completa  de  archivos  de  Kotlin.  El  código,  
sin  embargo,  no  se  compilará.  ¿Qué  líneas  no  compilarán?  ¿Qué  cambios  
necesita  hacer  en  las  definiciones  de  clase  e  interfaz  para  hacerlos?
¿compilar?

Nota:  No  puede  modificar  la  función  principal .

//Tipos  de  comida

comida  de  clase  abierta

clase  VeganFood:  Comida()

//Vendedores

Vendedor  de  interfaz<T>

clase  VendedorAlimentos:  Vendedor<Alimentos>

class  VendedorComidaVegana:  Vendedor<ComidaVegana>

//Consumidores

interfaz  Consumidor<T>

clase  Persona:  Consumidor<Comida>

clase  Vegano:  Consumidor<ComidaVegana>

diversión  principal(argumentos:  Array<String>)  {
var  foodSeller:  Vendedor<Comida>

VendedorComida  =  VendedorComida()

vendedor  de  comida  =  vendedor  de  comida  vegana  ()

var  veganFoodConsumer:  Consumidor<VeganFood>

ConsumidorComidaVegan  =  Vegano()

ConsumidorComidaVegan  =  Persona()

Respuestas  en  la  página  323.

estas  aqui  4 321
Machine Translated by Google

ser  la  solución  del  compilador

SER  la  solución  del  compilador
Aquí  hay  cuatro  clases  e  
interfaces  que  usan  genéricos.
Tu  trabajo  es  jugar  como  si  
fueras  el  
Compilador  y  
determinar  si  cada  
uno  compilará.  Si  
no  se  compila,  ¿por  qué  no?

A clase  A<en  T>(t:  T)  { Este  código  se  compila  correctamente  porque  el  tipo  T  contravariante  se  
fun  myFunction(t:  T)  { }
puede  usar  como  constructor  o  como  tipo  de  parámetro  de  función.
}

Este  código  no  se  compilará  porque  T  no  se  
B clase  B<en  T>(t:  T)  {
valor  x  =  t puede  usar  como  el  tipo  de  una  propiedad  val.

fun  myFunction(t:  T)  { }

C clase  abstracta  C<en  T>  { Este  código  no  se  compilará  porque  T  no  se  

fun  myFunction():  T  { } puede  usar  como  tipo  de  retorno  de  una  función.
}

Este  código  no  se  compilará  porque  T  no  se  
D clase  E<en  T>(t:  T)  {
puede  usar  como  el  tipo  de  una  propiedad  var.
var  y  =  t

fun  myFunction(t:  T)  { }

322  Capítulo  10
Machine Translated by Google

genéricos

A  continuación  se  muestra  una  lista  completa  de  archivos  de  Kotlin.  El  código,  
sin  embargo,  no  se  compilará.  ¿Qué  líneas  no  compilarán?  ¿Qué  cambios  
necesita  hacer  en  las  definiciones  de  clase  e  interfaz  para  hacerlos?
¿compilar?

Nota:  No  puede  modificar  la  función  principal .

//Tipos  de  comida

comida  de  clase  abierta

clase  VeganFood:  Comida()

//Vendedores

interfaz  Vendedor<out  T>

clase  VendedorAlimentos:  Vendedor<Alimentos>

class  VendedorComidaVegana:  Vendedor<ComidaVegana>

//Consumidores

interfaz  Consumidor<en  T>

clase  Persona:  Consumidor<Comida>

clase  Vegano:  Consumidor<ComidaVegana>

diversión  principal(argumentos:  Array<String>)  {
var  foodSeller:  Vendedor<Comida> Esta  línea  no  se  compilará,  ya  que  está  asignando  un  

VendedorComida  =  VendedorComida() Vendedor<Comida  vegana>  a  un  Vendedor<Comida>.
Para  hacerlo  compilar,  debemos  prefijar  T  sin  en  la  
vendedor  de  comida  =  vendedor  de  comida  vegana  ()
interfaz  del  vendedor.

var  veganFoodConsumer:  Consumidor<VeganFood>

ConsumidorComidaVegan  =  Vegano()

ConsumidorComidaVegan  =  Persona()

}
Esta  línea  no  se  compilará,  ya  que  está  asignando  un  
Consumidor<Comida>  a  un  Consumidor<ComidaVegana>.  Para  
hacerlo  compilar,  debemos  prefijar  T  con  in  en  la  interfaz  del  Consumidor.

estas  aqui  4 323
Machine Translated by Google

caja  de  herramientas

Tu  caja  de  herramientas  de  Kotlin

Puede  descargar  
Tiene  el  Capítulo  10  en  su  haber  y  ahora  ha   el  código  completo  
agregado  genéricos  a  su  caja  de  herramientas. del  capítulo  desde  
CAPÍTULO  
10
https://tinyurl.com/
HFKotlin.

Los  genéricos  le  permiten  escribir  código  coherente   Puede  definir  una  función  que  use  un  tipo  genérico  
que  es  seguro  para  tipos.  Colecciones  como  MutableList   fuera  de  una  declaración  de  clase  o  una  que  use  
usan  genéricos. un  tipo  genérico  diferente,  por  ejemplo:

El  tipo  genérico  se  define  entre  paréntesis  
diversión  <T>  listPet():  List<T>{
angulares  <>,  por  ejemplo:
...
}
Concurso  de  clase<T>
Un  tipo  genérico  es  invariable  si  solo  puede  aceptar  
Puede  restringir  el  tipo  genérico  a  un  supertipo  
referencias  de  ese  tipo  específico.  Los  tipos  genéricos  
específico,  por  ejemplo:
son  invariantes  por  defecto.

Concurso  de  clase<T:  Mascota> Un  tipo  genérico  es  covariante  si  puede  usar  un  
subtipo  en  lugar  de  un  supertipo.  Usted  especifica  que  
Crea  una  instancia  de  una  clase  con  un  tipo  genérico  
un  tipo  es  covariante  prefijándolo  con  out.
especificando  el  tipo  "real"  entre  paréntesis  angulares,  
por  ejemplo: Un  tipo  genérico  es  contravariante  si  puede  usar  un  
supertipo  en  lugar  de  un  subtipo.  Usted  especifica  que  
Concurso<Gato> un  tipo  es  contravariante  prefijándolo  con  in.

Siempre  que  sea  posible,  el  compilador  inferirá  
el  tipo  genérico.

324  Capítulo  10
Machine Translated by Google

11 lambdas  y  funciones  de  orden  superior

Tratando Código Como Datos

val  pastel  =  cocinar  
{ it.pastry()  
it.filling()  it.bake() }

¿Quiere  escribir  código  que  sea  aún  más  potente  y  flexible?
Si  es  así,  entonces  necesita  lambdas.  Una  expresión  lambda,  o  lambda,  es  un  bloque  de  código  
que  puede  pasar  como  un  objeto.  Aquí,  descubrirá  cómo  definir  una  lambda,  asignarla  a  una  
variable  y  luego  ejecutar  su  código.  Aprenderá  sobre  los  tipos  de  funciones  y  cómo  estos  pueden  
ayudarlo  a  escribir  funciones  de  orden  superior  que  usan  lambdas  para  sus  parámetros  o  valores  
devueltos.  Y  en  el  camino,  descubrirá  cómo  un  poco  de  azúcar  sintáctico  puede  hacer  que  su  vida  
de  codificación  sea  más  dulce.

este  es  un  nuevo  capitulo  325
Machine Translated by Google

introduciendo  lambdas

Introduciendo  lambdas
A  lo  largo  de  este  libro,  ha  visto  cómo  usar  las  funciones  integradas  
de  Kotlin  y  crear  las  suyas  propias.  Pero  a  pesar  de  que  hemos  cubierto  
mucho  terreno,  todavía  estamos  rascando  la  superficie.  Kotlin  tiene  un  
montón  de  funciones  que  son  incluso  más  poderosas  que  las  que  ya  has  
encontrado,  pero  para  usarlas,  hay  una  cosa  más  que  debes  aprender:  
cómo  crear  y  usar  expresiones  lambda.

Una  expresión  lambda,  o  lambda,  es  un  tipo  de  objeto  que  contiene  un  bloque  
de  código.  Puede  asignar  una  lambda  a  una  variable,  al  igual  que  cualquier  
otro  tipo  de  objeto,  o  pasar  una  lambda  a  una  función  que  luego  puede  
ejecutar  el  código  que  contiene.  Esto  significa  que  puede  usar  lambdas  para  
pasar  un  comportamiento  específico  a  una  función  más  generalizada.

Usar  lambdas  de  esta  manera  es  particularmente  útil  cuando  se  trata  
de  colecciones.  El  paquete  de  colecciones  tiene  una  función  sortBy  
incorporada,  por  ejemplo,  que  proporciona  una  implementación  genérica  
para  ordenar  MutableList;  usted  especifica  cómo  la  función  debe  ordenar  
la  colección  pasándole  una  lambda  que  describe  los  criterios:
Aquí  hay  una  lista  mutable  de  

artículos  de  comestibles  que   ÁRBITRO
¿Cómo  debo   Ordenarlos  por  
necesitan  clasificación. 0 ordenar  estos   precio  unitario.

ÁRBITRO
artículos  de  comestibles?
ÁRBITRO

1
comestibles
ÁRBITRO
.Ordenar  por() λ
valor
2
MutableList<Comestibles> La  función  sortBy()  sabe   lambda
cómo  ordenar  en  general...
...  y  la  lambda  le  dice  
qué  ordenar  
que  vamos  a  hacer
específicamente  en  esta  situación.
Antes  de  presentarle  las  funciones  integradas  que  usan  lambdas,  queremos  
que  se  familiarice  con  el  funcionamiento  de  las  lambdas,  por  lo  que  en  este  
capítulo  aprenderá  a  hacer  lo  siguiente:

1 Defina  una  lambda.
Descubrirá  cómo  se  ve  una  lambda,  cómo  asignarla  a  una  variable,  cuál  es  
su  tipo  y  cómo  invocar  el  código  que  contiene.

2 Crea  una  función  de  orden  superior.
Descubrirá  cómo  crear  una  función  que  tenga  un  parámetro  lambda  y  cómo  
usar  una  lambda  como  valor  de  retorno  de  una  función.

Comencemos  examinando  cómo  se  ve  una  lambda.

326  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Cómo  se  ve  el  código  lambda
Vamos  a  escribir  una  lambda  simple  que  suma  5  a  un  valor  de  
parámetro  Int.  Así  es  como  se  ve  la  lambda  para  esto:

Tirante  de  
apertura  de  la  lambda. Tirante  de  cierre  de  la  lambda.
{ x:  Int  ­>  x  +  5 }

Los  parámetros  de  la  lambda. El  cuerpo  de  la  lambda.  
Separa  los  
Aquí,  a  la  lambda  se  le   Aquí,  el  cuerpo  toma  x,  suma  
parámetros   5  y  lo  devuelve.
debe  dar  un  Int,  y  el  Int  se   del  cuerpo.
llama  x.

La  lambda  comienza  y  termina  con  llaves  {}.  Todas  las  lambdas  se  definen  
Tomo  un  parámetro  
entre  llaves,  por  lo  que  no  se  pueden  omitir.
Int  llamado  x.  Sumo  5  
Dentro  de  las  llaves,  la  lambda  define  un  solo  parámetro  Int  llamado  x  usando   a  x  y  devuelvo  el  
x:  Int.  Lambdas  puede  tener  parámetros  únicos  (como  es  el  caso  aquí),  múltiples   resultado.
parámetros  o  ninguno.

La  definición  del  parámetro  va  seguida  de  ­>.  ­>  se  usa  para  separar  cualquier  
parámetro  del  cuerpo.  Es  como  decir  "¡Oye,  parámetros,  haz  esto!" { x:  Int  ­>  x  +  5 }

Finalmente,  el  ­>  es  seguido  por  el  cuerpo  lambda,  en  este  caso,  x  +  5.
λ
Este  es  el  código  que  desea  que  se  ejecute  cuando  se  ejecute  la  lambda.   lambda
El  cuerpo  puede  tener  varias  líneas  y  la  última  expresión  evaluada  en  el  
cuerpo  se  usa  como  valor  de  retorno  de  la  lambda.

En  el  ejemplo  anterior,  la  lambda  toma  el  valor  de  x  y  devuelve  x  +  5.  Es  como  
escribir  la  función: Tomo  dos  
parámetros  Int  llamados  x  e  y.  
divertido  sumarCinco(x:  Int)  =  x  +  5
Los  sumo  y  devuelvo  el  resultado.

excepto  que  las  lambdas  no  tienen  nombre,  por  lo  que  son  anónimas.

Como  mencionamos  anteriormente,  las  lambdas  pueden  tener  múltiples  parámetros.
La  siguiente  lambda,  por  ejemplo,  toma  dos  parámetros  Int,  x  e  y,  y  devuelve  
el  resultado  de  x  +  y: { x:  Int,  y:  Int  ­>  x  +  y }

{ x:  Int,  y:  Int  ­>  x  +  y } λ
Si  la  lambda  no  tiene  parámetros,  puede  omitir  ­>.  La  siguiente  lambda,   lambda
por  ejemplo,  no  tiene  parámetros  y  simplemente  devuelve  la  cadena  "Pow!":

{ "¡Pum!" } Esta  lambda  no  tiene  parámetros,  por  lo  que  podemos  omitir  ­>.

Ahora  que  sabe  cómo  se  ve  una  lambda,  veamos  cómo  asigna  una  a  una  
variable.

estas  aqui  4 327
Machine Translated by Google

asignando  lambdas

Puedes  asignar  una  lambda  a  una  variable
Asignas  una  lambda  a  una  variable  de  la  misma  manera  que  asignas  
cualquier  otro  tipo  de  objeto  a  una  variable:  definiendo  la  variable  
usando  val  o  var,  y  luego  asignándole  la  lambda.
{ x:  Int  ­>  x  +  5 }
El  siguiente  código,  por  ejemplo,  asigna  una  lambda  a  una  nueva  
λ
ÁRBITRO
variable  llamada  addFive:

val  sumarCinco  =  { x:  Int  ­>  x  +  5 } agregar
lambda
Cinco
Hemos  definido  la  variable  addFive  usando  val,  por  lo  que  no  se  
puede  actualizar  para  contener  una  lambda  diferente.  Para  actualizar   Val  Lambda
la  variable,  debe  definirse  usando  var  así:

var  sumarCinco  =  { x:  Int  ­>  x  +  5 } Aquí,  podemos  asignar  una  nueva  lambda  a  
sumarCinco  =  { y:  Int  ­>  5  +  y } addFive  porque  hemos  definido  la  variable  usando  var.

Cuando  asigna  una  lambda  a  una  variable,  le  está  asignando  un  bloque  
de  código,  no  el  resultado  de  la  ejecución  del  código.  Para  ejecutar  el  
código  en  una  lambda,  debe  invocarlo  explícitamente.

No  te  preocupes  si  
Ejecute  el  código  de  un  lambda  invocándolo
lambda

Invoca  una  lambda  llamando  a  su  función  de  invocación,   las  expresiones  
pasando  los  valores  para  cualquier  parámetro.  El  siguiente  código,   parecen  un  poco

por  ejemplo,  define  una  variable  denominada  addInts  y  le  asigna   extraño  al  principio.
una  lambda  que  suma  dos  parámetros  Int.  Luego,  el  código  invoca  
Tómese  su  tiempo  y  trabaje  en  este  
la  lambda,  le  pasa  valores  de  parámetro  de  6  y  7,  y  asigna  el  resultado  
capítulo  a  un  ritmo  suave,  y  estará  bien.
a  una  nueva  variable  llamada  resultado:

val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }

val  resultado  =  addInts.invoke(6,  7)

También  puede  invocar  la  lambda  usando  el  siguiente  atajo:
13
ÁRBITRO

val  resultado  =  addInts(6,  7)

Esto  hace  lo  mismo  que: resultado En  t


val  resultado  =  addInts.invoke(6,  7)

pero  con  un  poco  menos  de  código.  Es  como  decir  "ejecutar  la   val  Int

expresión  lambda  contenida  en  variables  addInts  usando  valores  de  
parámetro  de  6  y  7".

Vayamos  detrás  de  escena  y  veamos  qué  sucede  cuando  invocas  una  
lambda.

328  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

¿Qué  sucede  cuando  invocas  una  lambda?
Cuando  ejecutas  el  código:

val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }
val  resultado  =  addInts(6,  7)

Suceden  las  siguientes  cosas:

1 val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }

Esto  crea  una  lambda  con  un  valor  de  { x:  Int,  y:  Int  ­>  x  +  y }.
Se  asigna  una  referencia  a  la  lambda  a  una  nueva  variable  llamada  addInts.

{ x:  Int,  y:  Int  ­>  x  +  y }

λ
ÁRBITRO

agregar
lambda
enteros

Val  Lambda

2 val  resultado  =  addInts(6,  7)

Esto  invoca  la  lambda  a  la  que  hace  referencia  addInts,  pasándole  valores  de  
6  y  7.  El  6  aterriza  en  el  parámetro  x  de  lambda  y  el  7  aterriza  en  el  parámetro  y  
de  lambda.

{ x:  Int,  y:  Int  ­>  x  +  y }

λ
ÁRBITRO

agregar ÁRBITRO
6
lambda
enteros

X En  t
Val  Lambda

val  Int

7
ÁRBITRO

y En  t

val  Int

estas  aqui  4 329
Machine Translated by Google

que  pasa

La  historia  continúa...
13
val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }

El  cuerpo  lambda  se  ejecuta  y  calcula  x  +  y.  La  lambda  crea  un  objeto  Int  con  
un  valor  de  13  y  devuelve  una  referencia  a  él.

Necesito  devolver  x  +  y.   6
ÁRBITRO
x  es  6  y  y  es  7,  por  lo  que  
devolveré  un  Int  de  13.
X En  t

val  Int
{ x:  Int,  y:  Int  ­>  x  +  y }

λ ÁRBITRO ÁRBITRO
7

lambda
y En  t
13

val  Int

En  t

4 val  resultado  =  addInts(6,  7)

El  valor  devuelto  por  la  lambda  se  asigna  a  una  nueva  variable  Int  llamada  
resultado.

{ x:  Int,  y:  Int  ­>  x  +  y }

λ
ÁRBITRO

agregar
lambda
enteros

Val  Lambda 13
ÁRBITRO

resultado En  t

val  Int

Ahora  que  sabe  lo  que  sucede  cuando  invoca  una  
lambda,  veamos  los  tipos  de  lambda.

330  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Las  expresiones  lambda  tienen  un  tipo
Al  igual  que  cualquier  otro  tipo  de  objeto,  una  lambda  tiene  un  tipo.  
Sin  embargo,  la  diferencia  con  el  tipo  de  lambda  es  que  no  especifica  
El  tipo  de  lambda  
un  nombre  de  clase  que  implementa  la  lambda.  En  su  lugar,   también  se  conoce  
especifica  el  tipo  de  parámetros  de  lambda  y  el  valor  de  retorno.

como  tipo  de  función.
El  tipo  de  una  lambda  toma  la  forma:

(parámetros)  ­>  tipo_retorno

Entonces,  si  tiene  una  lambda  con  un  solo  parámetro  Int  que  
devuelve  una  cadena  como  esta:

val  msg  =  { x:  Int  ­>  "El  valor  es  $x" }
{x:  Int  ­>
λ
ÁRBITRO
“El  valor  es  $x” }
su  tipo  es:

(Int)  ­>  Cadena mensaje
(Int)  ­>  Cadena

Cuando  asigna  una  lambda  a  una  variable,  el  compilador  infiere  el  tipo   El  tipo  de   El  tipo  de  


valor
de  variable  a  partir  de  la  lambda  que  se  le  asigna,  como  en  el  ejemplo  
(Int)  ­>  Cadena parámetro  de   retorno  de  la  lambda.
anterior.  Sin  embargo,  al  igual  que  cualquier  otro  tipo  de  objeto,  puede  
lambda.
definir  explícitamente  el  tipo  de  variable.  El  siguiente  código,  por  
ejemplo,  define  una  variable  denominada  add  que  puede  contener  
una  referencia  a  una  lambda  que  tiene  dos  parámetros  Int  y  devuelve  
un  Int: { x:  Int,  y:  Int  ­>  x  +  y }
λ
ÁRBITRO

valor  agregar:  (Int,  Int)  ­>  Int
agregar
sumar  =  { x:  Int,  y:  Int  ­>  x  +  y } (Int,  Int)  ­>  Int

Este  tipo  tiene  dos  parámetros  Int  y  un  
De  manera  similar,  el  siguiente  código  define  una  variable   valor
valor  de  retorno  Int.
denominada  saludo  que  puede  contener  una  referencia  a  una   (Int,  Int)  ­>  Int
lambda  sin  parámetros  y  un  valor  de  retorno  de  cadena:

saludo  val:  ()  ­>  Cadena
saludo  =  { "¡Hola!" } { "¡Hola!" }
λ
ÁRBITRO

Al  igual  que  con  cualquier  otro  tipo  de  declaración  de  variable,  puede   saludo ()  ­>  Cadena


declarar  explícitamente  el  tipo  de  una  variable  y  asignarle  un  valor  en  
una  sola  línea  de  código.  Esto  significa  que  puede  reescribir  el  código   Incluso  si  la  lambda  no  
valor
anterior  como:
tiene  parámetros,  su  
()  ­>  Cadena
val  saludo:  ()  ­>  Cadena  =  { "¡Hola!" } definición  de  tipo  aún  
incluye  los  ().

Declarar  la  variable. Asignarle  un  valor.

Especifique  su  tipo.

estas  aqui  4 331
Machine Translated by Google

tipo  de  inferencia

El  compilador  puede  inferir  
tipos  de  parámetros  lambda
Cuando  declara  explícitamente  el  tipo  de  una  variable,  puede  omitir  
cualquier  declaración  de  tipo  de  la  lambda  que  el  compilador  pueda  inferir.

Suponga  que  tiene  el  siguiente  código,  que  asigna  una  lambda  a  una  
Esta  lambda  suma  5  a  un  Int  llamado  x.
variable  llamada  addFive:

val  addFive:  (Int)  ­>  Int  =  { x:  Int  ­>  x  +  5 }

El  compilador  ya  sabe  por  la  definición  de  tipo  de  addFive  que  cualquier  
lambda  asignada  a  la  variable  debe  tener  un  parámetro  Int.  Esto  significa  que  
puede  omitir  la  declaración  de  tipo  Int  de  la  definición  del  parámetro  lambda  
porque  el  compilador  puede  inferir  su  tipo:
{ x  ­>  x  +  5 }
λ
ÁRBITRO

val  addFive:  (Int)  ­>  Int  =  { x  ­>  x  +  5 } agregar

Cinco (Int)  ­>  Int
El  compilador  sabe  que  x  debe  ser  un  
Int,  por  lo  que  podemos  omitir  su  tipo. valor

(Int)  ­>  Int
Puede  reemplazar  un  solo  parámetro  con  él
Si  tiene  una  lambda  que  tiene  un  solo  parámetro  y  el  compilador  puede  
inferir  su  tipo,  puede  omitir  el  parámetro  y  hacer  referencia  a  él  en  el  cuerpo  
de  lambda  usando  la  palabra  clave  it.

Para  ver  cómo  funciona  esto,  suponga,  como  se  indicó  anteriormente,  
que  tiene  una  lambda  asignada  a  una  variable  mediante  el  código:

val  addFive:  (Int)  ­>  Int  =  { x  ­>  x  +  5 }

Como  la  lambda  tiene  un  solo  parámetro,  x,  y  el  compilador  puede  inferir  que  x  
es  un  Int,  podemos  omitir  el  parámetro  x  de  la  lambda  y  reemplazarlo  con  él   { eso  +  5 }
λ
ÁRBITRO
en  el  cuerpo  de  lambda  de  esta  manera:

agregar
val  addFive:  (Int)  ­>  Int  =  { it  +  5 } (Int)  ­>  Int
Cinco
En  el  código  anterior,  { it  +  5 }  es  equivalente  a  { x  ­>  x  +  5 },  
valor
pero  es  mucho  más  conciso.
(Int)  ­>  Int
Tenga  en  cuenta  que  solo  puede  usar  la  sintaxis  it  en  situaciones  
en  las  que  el  compilador  puede  inferir  el  tipo  del  parámetro.  El  siguiente  
código,  por  ejemplo,  no  se  compilará  porque  el  compilador  no  puede  decir  
de  qué  tipo  debería  ser:

Esto  no  compilará  porque  el  
val  sumaCinco  =  { it  +  5 }
compilador  no  puede  inferir  su  tipo.

332  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Use  la  lambda  correcta  para  el  tipo  de  variable
Como  ya  sabe,  el  compilador  se  preocupa  profundamente  por  el  tipo  de  
variable.  Esto  se  aplica  a  los  tipos  lambda,  así  como  a  los  tipos  de  objetos  
simples,  lo  que  significa  que  el  compilador  solo  le  permitirá  asignar  una  
lambda  a  una  variable  que  sea  compatible  con  el  tipo  de  esa  variable.

Suponga  que  tiene  un  cálculo  con  nombre  de  variable  que  puede  
ÁRBITRO
contener  referencias  a  lambdas  con  dos  parámetros  Int  y  un  valor  de  retorno  
Int  como  este:
agregar

cálculo  de  valor:  (Int,  Int)  ­>  Int

valor
Si  intenta  asignar  una  lambda  al  cálculo  cuyo  tipo  no  coincide  con  el  
(Int,  Int)  ­>  Int
de  la  variable,  el  compilador  se  molestará.
El  siguiente  código,  por  ejemplo,  no  se  compilará  porque  la  lambda  
usa  Doubles  explícitamente: Esto  no  se  compilará,  porque  la  variable  
cálculo  =  { x:  Doble,  y:  Doble  ­>  x  +  y } de  cálculo  solo  aceptará  una  lambda  con  
dos  parámetros  Int  y  un  tipo  de  retorno  Int.

Use  Unit  para  decir  que  una  lambda  no  tiene  valor  de  retorno

Si  desea  especificar  que  una  lambda  no  tiene  valor  de  retorno,  puede  
hacerlo  declarando  que  su  tipo  de  retorno  es  Unidad.  La  siguiente  lambda,  
{ println(“¡Hola!”) }
por  ejemplo,  no  tiene  valor  de  retorno  e  imprime  el  texto  "¡Hola!"  cuando  se  
λ
ÁRBITRO

invoca:

miLambda ()  ­>  Unidad
val  myLambda:  ()  ­>  Unidad  =  { println("¡Hola!") }

valor
También  puede  usar  Unidad  para  especificar  explícitamente  que  no  
()  ­>  Unidad
desea  acceder  al  resultado  del  cálculo  de  una  lambda.  El  siguiente  código,  
por  ejemplo,  se  compilará,  pero  no  podrá  acceder  al  resultado  de  x  +  y:

cálculo  de  valor:  (Int,  Int)  ­>  Unidad  =  { x,  y  ­>  x  +  y }

P:  ¿El  código P:  ¿ Puedo  asignar  una  lambda  a  un P:  Que  su  sintaxis  parece  familiar.


val  x  =  { "¡Pow!" }  asigne  el  texto   variable  de  tipo  Any? ¿Lo  he  visto  antes?

"¡Pow!"  a  x?

R:  Sí.  Cualquier  variable  puede  aceptar  una   R:  ¡ Sí!  En  el  Capítulo  8  lo  usamos
A:  No.  Lo  anterior  asigna  una  lambda  a  x , referencia  a  cualquier  tipo  de  objeto,  incluidas   con  let.  No  te  lo  dijimos  en  ese  momento  
las  lambdas. porque  queríamos  que  te  concentraras  en  los  
y  no  una  cadena.  La  lambda,  sin  embargo,  
devuelve  "¡Pow!"  cuando  se  ejecuta. valores  nulos,  pero  let  es  en  realidad  una  función  
que  acepta  una  lambda  como  parámetro.

estas  aqui  4 333
Machine Translated by Google

prueba  de  manejo

Crear  el  proyecto  Lambdas
Ahora  que  ha  visto  cómo  crear  lambdas,  agreguemos  algunas  a  una  nueva  
aplicación.

Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asígnele  el  nombre  
"Lambdas".  Luego  cree  un  nuevo  archivo  de  Kotlin  llamado  Lambdas.kt  resaltando  
la  carpeta  src ,  haciendo  clic  en  el  menú  Archivo  y  eligiendo  Nuevo  →  Archivo/
clase  de  Kotlin.  Cuando  se  le  solicite,  nombre  el  archivo  "Lambdas"  y  seleccione  
Archivo  en  la  opción  Tipo.

A  continuación,  actualice  su  versión  de  Lambdas.kt  para  que  coincida  con  la  nuestra  a  continuación:

diversión  principal(argumentos:  Array<String>)  {
var  sumarCinco  =  { x:  Int  ­>  x  +  5 }
lambdas
println("Pase  6  para  agregarCinco:  ${agregarCinco(6)}")

origen

val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }

val  resultado  =  addInts.invoke(6,  7)
Lambdas.kt
println("Pase  6,  7  a  addInts:  $resultado")

*
valor  intLambda:  (Int,  Int)  ­>  Int  =  { x,  y  ­>  x y }

println("Pase  10,  11  a  intLambda:  ${intLambda(10,  11)}")

val  addSeven:  (Int)  ­>  Int  =  { it  +  7 }

println("Pase  12  para  sumarSiete:  ${sumarSiete(12)}")

val  myLambda:  ()  ­>  Unidad  =  { println("¡Hola!") }

miLambda()
}

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  
salida  del  IDE:

Pase  6  para  sumarCinco:  11

Pase  6,  7  a  addInts:  13
Pase  10,  11  a  intLambda:  110
Pase  12  para  sumar  Siete:  19
¡Hola!

334  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
diversión  principal(argumentos:  Array<String>)  {
valor  x  =  20

El  código  de  
valor  y  =  2.3

candidato  va  aquí.

Relaciona  
cada  candidato  
con  uno  de  los  
}
posibles  resultados.

Candidatos: Salida  posible:

valor  lam1  =  { x:  Int  ­>  x }  println(lam1(x  
+  6))
22.3

26
val  lam2:  (Doble)  ­>  Doble
lam2  =  {(it  *  2)  +  5} 9.6
println(lam2(y))

8.3

val  lam3:  (Doble,  Doble)  ­>  Unidad 1.1513.3
lam3  =  { x,  y  ­>  imprimirln(x  +  y) }  lam3.invocar(y,  
y) 9.3

10.013.3
var  lam4  =  { y:  Int  ­>  (y/2).toDouble() }  imprimir(lam4(x))

4.6
lam4  =  { it  +  6.3 }
imprimir  (lam4  (7))

estas  aqui  4 335
Machine Translated by Google

solución  de  mensajes  mixtos

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
Solución
diversión  principal(argumentos:  Array<String>)  {
valor  x  =  20

valor  y  =  2.3
El  código  de  
candidato  va  aquí.

Candidatos: Salida  posible:

valor  lam1  =  { x:  Int  ­>  x }  println(lam1(x  
+  6))
22.3

26
val  lam2:  (Doble)  ­>  Doble
lam2  =  {(it  *  2)  +  5} 9.6
println(lam2(y))

8.3

val  lam3:  (Doble,  Doble)  ­>  Unidad 1.1513.3
lam3  =  { x,  y  ­>  imprimirln(x  +  y) }  lam3.invocar(y,  
y) 9.3

10.013.3
var  lam4  =  { y:  Int  ­>  (y/2).toDouble() }  imprimir(lam4(x))

4.6
lam4  =  { it  +  6.3 }
imprimir  (lam4  (7))

336  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

¿Cuál  es  mi  tipo?
?
Aquí  hay  una  lista  de  definiciones  de  variables  y  una  lista  de  lambdas.  
¿Qué  lambdas  se  pueden  asignar  a  qué  variables?  Dibuja  líneas  que  conecten  
las  lambdas  con  sus  correspondientes  variables.

Definiciones  de  variables: lambda:

var  lambda1:  (Doble)  ­>  Int { it  +  7.1 }

var  lambda2:  (Int)  ­>  Doble { (es  *  3)  ­  4 }

var  lambda3:  (Int)  ­>  Int { x:  Int  ­>  x  +  56 }

var  lambda4:  (Doble)  ­>  Unidad { println("¡Hola!") }

var  lambda5 { x:  Doble  ­>  x  +  75 }

estas  aqui  4 337
Machine Translated by Google

¿Cuál  es  mi  tipo  de  solución?

¿Cuál  es  mi  tipo?
? Solución

Aquí  hay  una  lista  de  definiciones  de  variables  y  una  lista  de  lambdas.  
¿Qué  lambdas  se  pueden  asignar  a  qué  variables?  Dibuja  líneas  que  conecten  
las  lambdas  con  sus  correspondientes  variables.

Definiciones  de  variables: lambda:

var  lambda1:  (Doble)  ­>  Int { it  +  7.1 }

var  lambda2:  (Int)  ­>  Doble { (es  *  3)  ­  4 }

var  lambda3:  (Int)  ­>  Int { x:  Int  ­>  x  +  56 }

var  lambda4:  (Doble)  ­>  Unidad { println("¡Hola!") }

var  lambda5 { x:  Doble  ­>  x  +  75 }

338  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Puedes  pasar  una  lambda  a  una  función.
Además  de  asignar  una  lambda  a  una  variable,  también  puede  usar  uno  o  
más  como  parámetros  de  función.  Hacerlo  le  permite  pasar  un   Una  función  que  
comportamiento  específico  a  una  función  más  generalizada.
utiliza  una  lambda  
Para  ver  cómo  funciona  esto,  vamos  a  escribir  una  función  llamada  
convert  que  convierte  un  Double  usando  alguna  fórmula  que  se  le  pasa  
a  través  de  una  lambda,  imprime  el  resultado  y  lo  devuelve.  Esto  nos  
como  parámetro  o  valor  
permitirá,  por  ejemplo,  convertir  una  temperatura  de  Centígrados  a   de  retorno  se  conoce  
Fahrenheit,  o  convertir  un  peso  de  kilogramos  a  libras,  dependiendo  de  la  
fórmula  que  le  pasemos  en  el  argumento  lambda. como  función  de  orden  superior.

Comenzaremos  definiendo  los  parámetros  de  la  función.

Agregue  un  parámetro  lambda  a  una  función  
especificando  su  nombre  y  tipo
Necesitamos  decirle  a  la  función  de  conversión  dos  cosas  para  que  
convierta  un  Double  en  otro:  el  Double  que  queremos  convertir  y  la  lambda  
que  especifica  cómo  debe  convertirse.
Por  lo  tanto,  usaremos  dos  parámetros  para  la  función  de  conversión:  un  
Double  y  un  lambda.

Usted  define  un  parámetro  lambda  de  la  misma  manera  que  define  
cualquier  otro  tipo  de  parámetro  de  función:  especificando  el  tipo  de  
parámetro  y  dándole  un  nombre.  Nombraremos  nuestro  convertidor  lambda,  
y  como  queremos  que  lambda  convierta  un  Double  en  un  Double,  su  tipo  
debe  ser  (Double)  ­>  Double  (un  lambda  que  acepta  un  parámetro  Double  y  
devuelve  un  Double).

La  definición  de  la  función  (excluyendo  el  cuerpo  de  la  función)  se  encuentra  
a  continuación.  Como  puede  ver,  especifica  dos  parámetros,  un  Double  
llamado  x  y  un  convertidor  con  nombre  lambda,  y  devuelve  un  Double:

Este  es  el  parámetro  x,  un  Doble.

conversión  divertida  (x:  Doble,

convertidor:  (Doble)  ­>  Doble) :  Doble  {
Este  es  un  parámetro  
lambda  llamado  convertidor.  
La  función  devuelve  un  Doble.
Su  tipo  es  (Doble)  ­>  Doble.
//Código  para  convertir  el  Int

A  continuación,  escribiremos  el  código  para  el  cuerpo  de  la  función.

estas  aqui  4 339
Machine Translated by Google

función  de  conversión

Invocar  la  lambda  en  el  cuerpo  de  la  función.
Queremos  que  la  función  de  conversión  convierta  el  valor  del  
parámetro  x  usando  la  fórmula  que  se  le  pasa  a  través  del  
parámetro  de  conversión  (una  lambda).  Por  lo  tanto,  
invocaremos  el  convertidor  lambda  en  el  cuerpo  de  la  función,  
pasándole  el  valor  de  x,  y  luego  imprimiremos  y  devolveremos  
el  resultado.

Aquí  está  el  código  completo  para  la  función  de  conversión:

conversión  divertida  (x:  Doble,

Invoca  el  convertidor   convertidor:  (Doble)  ­>  Doble) :  Doble  {

con  nombre  lambda  y   val  resultado  =  convertidor(x)
Imprime  el  resultado.
asigna  su  valor  devuelto   println("$x  se  convierte  en  $resultado")
al  resultado.
resultado  devuelto
Devuelve  el  resultado.
}

Ahora  que  hemos  escrito  la  función,  intentemos  llamarla.

Llame  a  la  función  pasándole  valores  de  parámetro
Llamas  a  una  función  con  un  parámetro  lambda  de  la  misma  
manera  que  llamas  a  cualquier  otro  tipo  de  función:  pasándole  
un  valor  para  cada  argumento,  en  este  caso,  un  Double  y  un  
lambda.

Usemos  la  función  de  conversión  para  convertir  20.0  grados  
centígrados  a  Fahrenheit.  Para  hacer  esto,  pasaremos  valores  
de  20.0  y  { c:  Double  ­>  c  *  1.8  +  32 }  a  la  función:

convert(20.0,  { c:  Doble  ­>  c  *  1.8  +  32 })

Este  es  el  valor  que  
queremos  convertir... ...y  esta  es  la  lambda  que  usaremos  para  convertirla.  Tenga  en  cuenta  
que  podríamos  usar  "it"  en  lugar  de  c  porque  la  lambda  usa  un  solo  
parámetro  cuyo  tipo  puede  inferir  el  compilador.

Cuando  se  ejecuta  el  código  anterior,  devuelve  un  valor  de  
68,0  (el  valor  de  20,0  grados  centígrados  cuando  se  convierte  
a  Fahrenheit).

Vayamos  detrás  de  escena  y  analicemos  lo  que  sucede  cuando  
se  ejecuta  el  código.

340  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

¿Qué  sucede  cuando  llamas  a  la  función?
Lo  siguiente  sucede  cuando  llama  a  la  función  de  conversión  
usando  el  código:

val  fahrenheit  =  convert(20.0,  { c:  Doble  ­>  c *  1.8  +  32 })

1 valor  farenheit  =  convert(20.0,  { c:  Doble  ­>  c  *  1.8  +  32 })

Esto  crea  un  objeto  Double  con  un  valor  de  20.0  y  una  lambda  con  un  valor  de  { c:  Double  ­>  
c *  1.8  +  32 }.

20.0 { c:  Doble  ­>  c  *  1.8  +  32 }

λ
Doble (Doble)  ­>  Doble

2 conversión  divertida  (x:  Doble,  convertidor:  (Doble)  ­>  Doble) :  Doble  {
val  resultado  =  convertidor(x)  
println("$x  se  convierte  en  $resultado")  devolver  
resultado
}

El  código  pasa  referencias  a  los  objetos  que  se  crean  a  la  función  de  conversión.
El  Double  aterriza  en  el  parámetro  x  de  la  función  de  conversión  y  el  lambda  aterriza  
en  su  parámetro  de  conversión.  Luego,  el  código  invoca  el  convertidor  lambda,  usando  
x  como  parámetro  de  lambda.

ÁRBITRO 20.0

Introduciré  x  en  la  
fórmula  de  mi  cuerpo.
X
Doble

valor  doble
{ c:  Doble  ­>  c  *  1.8  +  32 }

Estos  son  los  parámetros  de  
ÁRBITRO
λ
las  funciones  de  conversión.
convertidor (Doble)  ­>  Doble

val  (Doble)  ­>  Doble

estas  aqui  4 341
Machine Translated by Google

que  pasa

La  historia  continúa...
3 fun  convert(x:  Doble,  convertidor:  (Doble)  ­>  Doble) :  Doble  { val  resultado  =  
convertidor(x)  println("$x  se  convierte  en  $resultado")  devolver  resultado

El  cuerpo  de  la  lambda  se  ejecuta  y  su  resultado  (un  Double  con  un  valor  de  68,0)  
se  asigna  a  una  nueva  variable  denominada  resultado.  La  función  imprime  los  
valores  de  x  y  las  variables  de  resultado  y  devuelve  una  referencia  al  objeto  de  resultado.

ÁRBITRO 20.0 Mi  valor  de  retorno  


20.0 * es  1.8  +  32  =  68.0

X
Doble

valor  doble { c:  Doble  ­>  c  *  1.8  +  32 }
ÁRBITRO
λ
convertidor (Doble)  ­>  Doble 68.0
ÁRBITRO

val  (Doble)  ­>  Doble
resultado
Doble

valor  doble La  función  imprime  
este  valor  y  devuelve  
una  referencia  a  él.
4 valor  farenheit  =  convert(20.0,  { c:  Doble  ­>  c  *  1.8  +  32 })

Se  crea  una  nueva  variable  Fahrenheit.  Se  le  asigna  una  referencia  al  objeto  
devuelto  por  la  función  de  conversión.

ÁRBITRO 68.0

farenheit
Doble

valor  doble

Ahora  que  ha  visto  lo  que  sucede  cuando  llama  a  una  
función  con  un  parámetro  lambda,  veamos  algunos  
atajos  que  puede  tomar  cuando  llama  a  este  tipo  de  función.

342  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Puedes  mover  la  lambda  FUERA  de  ()...
Hasta  ahora,  ha  visto  cómo  llamar  a  una  función  con  un  
parámetro  lambda  pasando  argumentos  a  la  función  dentro  de  
los  paréntesis  de  la  función.  Llamamos  a  la  función  de  conversión,  
por  ejemplo,  usando  el  siguiente  código:

convert(20.0,  { c:  Doble  ­>  c *  1.8  +  32 })

Si  el  parámetro  final  de  una  función  que  desea  llamar  es  una  lambda,  
como  es  el  caso  de  nuestra  función  de  conversión,  puede  mover  el  
argumento  lambda  fuera  de  los  paréntesis  de  la  llamada  a  la  función.  El  
siguiente  código,  por  ejemplo,  hace  lo  mismo  que  el  código  anterior,  pero  
hemos  movido  la  lambda  fuera  de  los  paréntesis:

convert(20.0)  { c:  Doble  ­>  c  *  1.8  +  32 }
La  lambda  ya  no  está  encerrada  por  el  
paréntesis  de  cierre  de  la  función.
Aquí  está  el  paréntesis  de  cierre  de  la  función.

...  o  eliminar  los  ()  por  completo
Si  tiene  una  función  que  tiene  solo  un  parámetro,  y  ese  parámetro  
es  una  lambda,  puede  omitir  los  paréntesis  por  completo  cuando  
llame  a  la  función.

Para  ver  cómo  funciona  esto,  suponga  que  tiene  la  siguiente  
función  llamada  convertFive  que  convierte  el  Int  5  en  Double  
usando  una  fórmula  de  conversión  que  se  le  pasa  a  través  de  una  
lambda.  Aquí  está  el  código  para  la  función:

diversión  convertFive(convertidor:  (Int)  ­>  Doble) :  Doble  {

val  resultado  =  convertidor(5)

println("5  se  convierte  en  $resultado")
resultado  devuelto

Como  la  función  convertFive  tiene  un  solo  parámetro,  una  
lambda,  puede  llamar  a  la  función  de  esta  manera:

convertFive  { it  *  1.8  +  32 }
Observe  que  no  hay  paréntesis  en  esta  
llamada  de  función.  Esto  es  posible  porque  
Esto  hace  lo  mismo  que: la  función  tiene  un  solo  parámetro,  que  es  un
lambda.
convertFive()  { it  *  1.8  +  32 }

pero  hemos  quitado  los  paréntesis.

Ahora  que  ha  aprendido  a  escribir  una  función  que  usa  un  parámetro  
lambda,  actualicemos  el  código  de  nuestro  proyecto.

estas  aqui  4 343
Machine Translated by Google

código  de  proyecto

Actualice  el  proyecto  Lambdas
Agregaremos  las  funciones  convert  y  convertFive  a  nuestro  proyecto  Lambdas.  Actualice  

su  versión  de  Lambdas.kt  en  el  proyecto  para  que  coincida  con  la  nuestra  a  continuación  
(nuestros  cambios  están  en  negrita):

conversión  divertida  (x:  Doble,
convertidor:  (Doble)  ­>  Doble) :  Doble  {
val  resultado  =  convertidor(x)
println("$x  se  convierte  en  $resultado")
resultado  devuelto

} Suma  estas  dos  funciones.

diversión  convertFive(convertidor:  (Int)  ­>  Doble) :  Doble  {
val  resultado  =  convertidor(5)
println("5  se  convierte  en  $resultado")
resultado  devuelto

} lambdas

diversión  principal(argumentos:  Array<String>)  { origen

var  sumarCinco  =  { x:  Int  ­>  x  +  5 }
println("Pase  6  para  agregarCinco:  ${agregarCinco(6)}")
Lambdas.kt

val  addInts  =  { x:  Int,  y:  Int  ­>  x  +  y }
val  resultado  =  addInts.invoke(6,  7)
println("Pase  6,  7  a  addInts:  $resultado")
Ya  no  

necesitamos  
*
estas  líneas,   valor  intLambda:  (Int,  Int)  ­>  Int  =  { x,  y  ­>  x y }

por  lo  que   println("Pase  10,  11  a  intLambda:  ${intLambda(10,  11)}")
puede  eliminarlas.
val  addSeven:  (Int)  ­>  Int  =  { it  +  7 }
println("Pase  12  para  sumarSiete:  ${sumarSiete(12)}")

val  myLambda:  ()  ­>  Unidad  =  { println("¡Hola!") }
miLambda()

convert(20.0)  { it  *  1.8  +  32 } Añade  estas  líneas.  Tenga  en  cuenta  que  
convertFive  { it  *  1.8  +  32 } podemos  usar  "it"  porque  cada  lambda  usa  un  solo  
} parámetro  cuyo  tipo  puede  inferir  el  compilador.

Tomemos  el  código  para  una  prueba  de  manejo.

344  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  
de  salida  del  IDE:
20.0  se  convierte  a  68.0
5  se  convierte  a  41.0

Antes  de  ver  qué  más  puede  hacer  con  las  lambdas,  pruebe  el  siguiente  
ejercicio.

Formato  lambda  de  cerca
Como  dijimos  anteriormente  en  el  
capítulo,  un  cuerpo  lambda  puede  incluir  varias  
P:  Parece  que  hay  bastantes  atajos
líneas  de  código.  La  siguiente  lambda,  por  
puedes  tomar  cuando  usas  lambdas.  ¿Realmente  
ejemplo,  imprime  el  valor  de  su  parámetro  y   necesito  saber  acerca  de  todos  ellos?
luego  lo  usa  en  un  cálculo:

{ c:  Doble  ­>  println(c) R:  Es  útil  conocer  estos  atajos  porque
una  vez  que  se  acostumbre  a  ellos,  pueden  hacer  
C *  1.8  +  32 } que  su  código  sea  más  conciso  y  legible.  La  sintaxis  
alternativa  que  está  diseñada  para  hacer  que  su  código  
Cuando  tiene  una  lambda  cuyo  cuerpo   sea  más  fácil  de  leer  a  veces  se  denomina  azúcar  
tiene  varias  líneas,  la  última  expresión   sintáctico,  ya  que  puede  hacer  que  el  lenguaje  sea  más  

evaluada  se  usa  como  valor  de  retorno  de  la   "dulce"  para  los  humanos.  Pero  incluso  si  no  desea  utilizar  
los  accesos  directos  que  hemos  discutido  en  su  propio  
lambda.  Entonces,  en  el  ejemplo  anterior,  el  
código,  vale  la  pena  conocerlos  porque  puede  encontrarlos  en  código  de  tercer
valor  de  retorno  se  define  usando  la  línea:

C *  1.8  +  32 P:  ¿ Por  qué  las  lambdas  se  llaman  lambdas?

Una  lambda  también  se  puede  
R:  Es  porque  vienen  de  un  área  de
formatear  para  que  parezca  un  bloque  de   matemáticas  e  informática  llamado  Lambda  
código,  con  las  llaves  que  la  rodean  en   Calculus,  donde  las  funciones  pequeñas  y  
líneas  diferentes  al  contenido  de  la   anónimas  se  representan  con  la  letra  griega  λ  (a  lambda).
lambda.  El  siguiente  código  usa  esta  técnica  
para  pasar  el  lambda  { it  *  1.8  +  32 }  a  la   P:  ¿ Por  qué  las  lambdas  no  se  denominan  funciones?
función  convertFive:

R:  Una  lambda  es  un  tipo  de  función,  pero  en  la  mayoría
convertircinco  { lenguajes,  las  funciones  siempre  tienen  nombres.  
es  *  1.8  +  32 Como  ya  has  visto,  una  lambda  no  necesita  tener  un  nombre.

estas  aqui  4 345
Machine Translated by Google

rompecabezas  de  la  piscina

Rompecabezas  de  piscina

Su  trabajo  es  tomar  fragmentos  de  código  del  grupo  y  colocarlos  en  las  líneas  en  
blanco  del  código.  No  puede  usar  el  mismo  fragmento  de  código  más  de  una  
vez  y  no  necesitará  usar  todos  los  fragmentos  de  código.  Su  objetivo  es  crear  
una  función  nombrada  a  menos  que  sea  llamada  por  la  función  principal  a  
continuación.  La  función  a  menos  que  tenga  dos  parámetros,  una  condición  con  
nombre  booleano  y  un  código  con  nombre  lambda .  La  función  debe

invoque  el  código  lambda  cuando  la  condición  sea  falsa.

divertido  a  menos  que  ( , código: ) {

si  ( ) {

diversión  principal(argumentos:  Array<String>)  {
val  options  =  arrayOf("Rojo",  "Ambar",  "Verde")
var  crossWalk  =  opciones[(Math.random()  *  opciones.tamaño).toInt()]
if  (paso  de  peatones  ==  "Verde")  {

println("¡Camina!")
}
Imprimir  "¡Alto!"  a  menos  que  CrossWalk  ==  “Verde”.
a  menos  que  (paso  de  peatones  ==  "Verde")  {

println("¡Alto!")
}

Nota:  ¡cada  cosa  del  grupo  
solo  se  puede  usar  una  vez!

Vacío

Unidad
: !condición
condición
código
Nulo
condición booleano
() ()
código()
­>

Respuestas  en  la  página  360.

346  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Una  función  puede  devolver  una  lambda

Además  de  usar  una  lambda  como  parámetro,  una  
función  también  puede  devolver  una  especificando  el  tipo  
de  lambda  como  su  tipo  de  retorno.  El  siguiente  código,  por  
ejemplo,  define  una  función  denominada  getConversionLambda  
que  devuelve  una  lambda  de  tipo  (Doble)  ­>  Doble.  La  
lambda  exacta  que  devuelve  la  función  depende  del  valor  
de  la  cadena  que  se  le  pasa.
Devuelve  una  lambda  cuyo  tipo  

La  función  tiene  un  parámetro,  una  cadena. es  (Doble)  ­>  Doble.

divertido  getConversionLambda(str:  String):  (Doble)  ­>  Doble  {
if  (str  ==  "Centígrados  a  Fahrenheit")  {
devolver  {es  *  1.8  +  32}
λ
}  else  if  (str  ==  "KgsToPounds")  { (Doble)  ­>  Doble
devolver  {es  *  2.204623}
}  else  if  (str  ==  "Libras  a  Toneladas")  { La  función  devuelve  una  de  
volver  { it /  2000.0 }
estas  lambdas,  según  el  valor  
de  la  cadena  que  se  le  pasa.
}  demás  {
devolverlo }
}
}

Puede  invocar  la  lambda  devuelta  por  una  función  o  usarla  
como  argumento  para  otra  función.  El  siguiente  código,  por  
ejemplo,  invoca  el  valor  de  retorno  de  getConversionLambda  
para  obtener  el  valor  de  2,5  kilogramos  en  libras  y  lo  asigna  
a  una  variable  llamada  libras:

val  libras  =  getConversionLambda("KgsToPounds"))(2.5)

Esto  llama  a  la  función  getConversionLambda... ...y  esto  invoca  la  lambda  
devuelta  por  la  función.
Y  el  siguiente  ejemplo  usa  getConversionLambda  para  
obtener  una  lambda  que  convierte  una  temperatura  de   Aquí,  estamos  pasando  el  valor  de  retorno  de  
Centígrados  a  Fahrenheit,  y  luego  la  pasa  a  la  función  de  conversión: getConversionLambda  a  la  función  de  conversión.

convert(20.0,  getConversionLambda("CentigradeToFahrenheit"))

Incluso  puede  definir  una  función  que  reciba  y  devuelva
una  lambda  Veremos  esto  a  continuación.

estas  aqui  4 347
Machine Translated by Google

función  de  combinación

Escriba  una  función  
que  reciba  Y  devuelva  lambdas
λ λ
Vamos  a  crear  una  función  llamada  combine  que  toma  dos  parámetros  lambda,  
los  combina  y  devuelve  el  resultado  (otro  lambda).  Si  la  función  recibe  lambdas   Kgs  a  Libras Libras  a  Toneladas  estadounidenses

para  convertir  un  valor  de  kilogramos  a  libras  y  convertir  un  valor  de  libras  a  
toneladas,  devolverá  una  lambda  que  convierte  un  valor  de  kilogramos  a  toneladas   Crearemos  
estadounidenses. una  función   combinar()
Entonces  podremos  usar  esta  lambda  en  otro  lugar  de  nuestro  código. que  combine  
dos  lambdas  
Comenzaremos  definiendo  los  parámetros  de  la  función  y  el  tipo  de  devolución.
en  una  sola  lambda.

Definir  los  parámetros  y  el  tipo  de  retorno
λ
Kg  a  Toneladas  estadounidenses
Todas  las  lambdas  utilizadas  por  la  función  de  combinación  deben  convertir  un  
valor  Double  en  otro  valor  Double,  por  lo  que  cada  una  tiene  un  tipo  de  (Double)  
­>  Double.  Por  lo  tanto,  nuestra  definición  de  función  debe  verse  así:
La  función  de  combinación  tiene  dos  
parámetros  lambda  de  tipo  (Doble)  ­>  Doble.
combinación  divertida  (lambda1:  (Doble)  ­>  Doble,

lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {
//Código  para  combinar  las  dos  lambdas
La  función  también  devuelve  
}
una  lambda  de  este  tipo.
A  continuación,  veamos  el  cuerpo  de  la  función.

Definir  el  cuerpo  de  la  función
El  cuerpo  de  la  función  debe  devolver  una  lambda,  y  esta  lambda  debe  tener  las  
siguientes  características:

¥  Debe  tomar  un  parámetro,  un  Doble.  Llamaremos  a  este  parámetro  x.

¥ El  cuerpo  de  la  lambda  debe  invocar  lambda1,  pasándole  el  valor  de  x.
El  resultado  de  esta  invocación  debe  pasarse  a  lambda2.

Esto  lo  podemos  lograr  usando  el  siguiente  código:

combinación  divertida  (lambda1:  (Doble)  ­>  Doble,

lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {

volver  { x:  Doble  ­>  lambda2(lambda1(x)) }

}
x  se  pasa  a  lambda1,  que  acepta  y  devuelve  un  Double.  
La  lambda  devuelta  por  combine  
Luego,  el  resultado  se  pasa  a  lambda2,  que  también  
toma  un  parámetro  Double  llamado  x.
acepta  y  devuelve  un  Double.

Escribamos  un  código  que  use  la  función.

348  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Cómo  usar  la  función  combinar
La  función  de  combinación  que  acabamos  de  crear  toma  dos  lambdas  
y  las  combina  para  formar  una  tercera.  Esto  significa  que  si  pasamos  la  
función  una  lambda  para  convertir  un  valor  de  kilogramos  a  libras  y  otra  
para  convertir  un  valor  de  libras  a  toneladas  estadounidenses,  la  función  
devolverá  una  lambda  que  convierte  un  valor  de  kilogramos  a  toneladas  
estadounidenses.
Aquí  está  el  código  para  hacer  esto:

//Definir  dos  lambdas  de  conversión Estas  lambdas  convierten  un  Double  

val  kgsToPounds  =  { x:  Double  ­>  x  *  2.204623 }  val  librasToUSTons  =   de  kilogramos  a  libras  y  de  libras  a  
toneladas  estadounidenses.
{ x:  Double  ­>  x /  2000.0 }

//Combina  las  dos  lambdas  para  crear  una  nueva Pase  las  lambdas  a  la  función  de  
combinación.  Esto  produce  una  lambda  
val  kgsToUSTons  =  combinar(kgsToLibras,  librasToUSTons)
que  convierte  un  Doble  de  kilogramos  a  
Toneladas  estadounidenses.
//Invoque  la  lambda  kgsToUSTons
val  usTons  =  kgsToUSTons(1000.0) //1.1023115
Invoque  la  lambda  resultante  
pasándole  un  valor  de  1000,0.
Esto  devuelve  1.1023115.
Vayamos  entre  bastidores  y  veamos  qué  sucede  cuando  el  código
carreras.

Qué  sucede  cuando  se  ejecuta  el  código

1 val  kgsToLibras  =  { x:  Doble  ­>  x  *  2.204623 }  val  librasToUSTons  =  
{ x:  Doble  ­>  x /  2000.0 }  val  kgsToUSTons  =  combine(kgsToLibras,  
librasToUSTons)

Esto  crea  dos  variables  y  asigna  una  lambda  a  cada  una.  A  continuación,  se  pasa  una  
referencia  a  cada  lambda  a  la  función  de  combinación.

{ x:  Doble  ­>  x  *  2.204623 }

λ
ÁRBITRO

kgsA
Libras (Doble)  ­>  Doble { x:  Doble  ­>  x /  2000.0 }

λ
ÁRBITRO

val  (Doble)  ­>  Doble
libras  a
UStoneladas
(Doble)  ­>  Doble

val  (Doble)  ­>  Doble
estas  aqui  4 349
Machine Translated by Google

que  pasa

La  historia  continúa...
2 combinación  divertida  (lambda1:  (Doble)  ­>  Doble,
lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {
volver  { x:  Doble  ­>  lambda2(lambda1(x)) }
}

La  lambda  kgsToPounds  aterriza  en  el  parámetro  lambda1  de  la  función  de  
combinación,  y  la  lambda  librasToUSTons  aterriza  en  su  parámetro  lambda2.

{ x:  Doble  ­>  x  *  2.204623 }

λ
ÁRBITRO

lambda1 (Doble)  ­>  Doble

{ x:  Doble  ­>  x /  2000.0 }
val  (Doble)  ­>  Doble
λ
ÁRBITRO

Estos  son  los  parámetros  
lambda2 (Doble)  ­>  Doble
de  la  función  de  combinación.

val  (Doble)  ­>  Doble

3 combinación  divertida  (lambda1:  (Doble)  ­>  Doble,
lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {
volver  { x:  Doble  ­>  lambda2(lambda1(x)) }
}

se  ejecuta  lambda1(x).  Como  el  cuerpo  de   *  2.204623,  donde  x  es  un
lambda1  es  x  Double,  esto  crea  un  objeto  Double  con  un  valor  d*  
2.204623.
e  x

{ x:  Doble  ­>  x  *  2.204623 }

λ
ÁRBITRO

lambda1 (Doble)  ­>  Doble
*  2.204623  x
val  (Doble)  ­>  Doble

Doble

350  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

La  historia  continúa...

4 combinación  divertida  (lambda1:  (Doble)  ­>  Doble,
lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {
volver  { x:  Doble  ­>  lambda2(lambda1(x)) }
}

El  objeto  Double  con  un  valor  de  x  *  2.204623  luego  se  pasa  a  lambda2.
Como  el  cuerpo  de  lambda2  es  x /  2000.0,  esto  significa  que  x  *  2.204623  
se  sustituye  por  x.  Esto  crea  un  Doble  con  un  valor  de  (x  *  2.204623) /  
2000.0,  o  x  *  0.0011023115.

{ x:  Doble  ­>  x /  2000.0 }

λ
ÁRBITRO

lambda2 (Doble)  ­>  Doble X *  0.0011023115

val  (Doble)  ­>  Doble
Doble

5 combinación  divertida  (lambda1:  (Doble)  ­>  Doble,
lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  { return  { x:  
Doble  ­>  lambda2(lambda1(x)) }
}

Esto  crea  la  referencia  lambda  { x:  Double  ­>  x   *  0.0011023115 },  y  un
a  esta  lambda  devuelta  por  la  función.

{ x:  Doble  ­>  x /  0.0011023115 }

λ
(Doble)  ­>  Doble

estas  aqui  4 351
Machine Translated by Google

tipo  de  alias

La  historia  continúa...
6 val  kgsToUSTons  =  combinar(kgsToLibras,  librastoUSTons)  val  usTons  =  
kgsToUSTons(1000.0)

La  lambda  devuelta  por  la  función  de  combinación  se  asigna  a  una  variable  denominada  
kgsToUSTons.  Se  invoca  con  un  argumento  de  1000,0,  que  devuelve  un  valor  de  1,1023115.  
Esto  se  asigna  a  una  nueva  variable  llamada  usTons.

{ x:  Doble  ­>  x /  0.0011023115 }

λ
ÁRBITRO

kgsA
UStoneladas
(Doble)  ­>  Doble

val  (Doble)  ­>  Doble

1.1023115
ÁRBITRO

nosotros  Toneladas
Doble

Doble

Puede  hacer  que  el  código  lambda  sea  más  legible

Estamos  casi  al  final  del  capítulo,  pero  antes  de  continuar,  hay  una  cosa  
más  que  queremos  mostrarle:  cómo  hacer  que  su  código  lambda  sea  más  
legible.

Cuando  usa  tipos  de  función  (el  tipo  de  tipo  que  se  usa  para  definir  una  
lambda),  puede  hacer  que  su  código  sea  engorroso  y  menos  legible.
La  función  de  combinación,  por  ejemplo,  contiene  múltiples  referencias  al   La  función  de  combinación  tiene  
tipo  de  función  (Doble)  ­>  Doble: tres  instancias  del  tipo  de  
función  (Doble)  ­>  Doble.
combinación  divertida  (lambda1:  (Doble)  ­>  Doble,

lambda2:  (Doble)  ­>  Doble):  (Doble)  ­>  Doble  {

volver  { x:  Doble  ­>  lambda2(lambda1(x)) }

Sin  embargo,  puede  hacer  que  su  código  sea  más  legible  reemplazando  el  
tipo  de  función  con  un  alias  de  tipo.  Veamos  qué  es  esto  y  cómo
usa  uno

352  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Use  typealias  para  proporcionar  un  nombre  
diferente  para  un  tipo  existente
Un  alias  de  tipo  le  permite  proporcionar  un  nombre  alternativo  para  un  tipo  existente,  
que  luego  puede  usar  en  su  código.  Esto  significa  que  si  su  código  usa  un  tipo  de  
función  como  (Doble)  ­>  Doble,  puede  definir  un  alias  de  tipo  que  se  usa  en  su  lugar,  lo  
que  hace  que  su  código  sea  más  legible.

Defina  un  alias  de  tipo  utilizando  la  palabra  clave  typealias .  Así  es  como,  por  

λ
ejemplo,  lo  usa  para  definir  un  alias  de  tipo  llamado  DoubleConversion  que  
podemos  usar  en  lugar  del  tipo  de  función  (Double)  ­>  Double:

(Doble)  ­>  Doble
Este  tipo  de  alias  
typealias  DoubleConversion  =  (Doble)  ­>  Doble
significa  que  podemos  usar  

DoubleConversion  en  lugar  de  
Esto  significa  que  nuestras  funciones  de  conversión  y  combinación  ahora  pueden  
(Double)  ­>  Double.
convertirse  en:

conversión  divertida  (x:  Doble,

convertidor:  DoubleConversion) :  Doble  {
λ
val  resultado  =  convertidor(x) Conversión  doble
println("$x  se  convierte  en  $resultado") Podemos  usar  el  

resultado  devuelto alias  de  tipo  
DoubleConversion  en  las  
}
funciones  de  conversión  y  
combinación  para  hacer  que  el  código  sea  más  legible.
combinación  divertida  (lambda1:  DoubleConversion,

lambda2:  DoubleConversion):  DoubleConversion  {

volver  { x:  Doble  ­>  lambda2(lambda1(x)) }

Cada  vez  que  el  compilador  ve  el  tipo  DoubleConversion,  sabe  que  es  un  marcador  
de  posición  para  el  tipo  (Double)  ­>  Double.
Las  funciones  de  conversión  y  combinación  anteriores  hacen  lo  mismo  que  antes,  pero  el  
código  es  más  legible.

Puede  usar  typealias  para  proporcionar  un  nombre  alternativo  para  cualquier  tipo,  no  
solo  para  los  tipos  de  funciones.  Puedes,  por  ejemplo,  usar:

typealias  DuckArray  =  Array<Duck>

para  que  pueda  referirse  al  tipo  DuckArray  en  lugar  de  Array<Duck>.

Actualicemos  el  código  en  nuestro  proyecto.

estas  aqui  4 353
Machine Translated by Google

código  de  proyecto

Actualice  el  proyecto  Lambdas
Agregaremos  el  alias  de  tipo  DoubleConversion  y  las  funciones  
getConversionLambda  y  combine  a  nuestro  proyecto  Lambdas,  junto  con  
algún  código  que  las  use.
Actualice  su  versión  de  Lambdas.kt  en  el  proyecto  para  que  coincida  con  la  
nuestra  a  continuación  (nuestros  cambios  están  en  negrita):
Agregue  el  typealias.
typealias  DoubleConversion  =  (Doble)  ­>  Doble

Reemplace  el  tipo  de  función  con  el  alias  de  tipo.
conversión  divertida  (x:  Doble,

convertidor:  (Double)  ­>  Double  DoubleConversion) :  Double  {

val  resultado  =  convertidor(x)

println("$x  se  convierte  en  $resultado")
resultado  devuelto

}
Elimina  esta  función  porque  ya  no  la  necesitamos.

diversión  convertFive(convertidor:  (Int)  ­>  Doble) :  Doble  {

val  resultado  =  convertidor(5)

println("5  se  convierte  en  $resultado")
resultado  devuelto

}
Agregue  la  función  getConversionLambda.

diversión  getConversionLambda(str:  String):  DoubleConversion  {

if  (str  ==  "Centígrados  a  Fahrenheit")  {

devolver  {es  *  1.8  +  32}

}  else  if  (str  ==  "KgsToPounds")  {

devolver  {es  *  2.204623} lambdas

}  else  if  (str  ==  "Libras  a  Toneladas")  {
origen
volver  { it /  2000.0 }

}  demás  {
Lambdas.kt
devolverlo }

}
Agregue  la  función  de  combinación.

combinación  divertida  (lambda1:  DoubleConversion,

lambda2:  DoubleConversion):  DoubleConversion  {

volver  { x:  Doble  ­>  lambda2(lambda1(x)) } El  código  continúa  en  la  

} página  siguiente.

354  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

El  código  continuó...
lambdas
diversión  principal(argumentos:  Array<String>)  {
Eliminar  estas  líneas.
convert(20.0)  { it  *  1.8  +  32 }
origen

convertFive  { it  *  1.8  +  32 }

Lambdas.kt
//Convertir  2.5kg  a  Libras
println("Convertir  2,5  kg  a  libras:  ${getConversionLambda("KgsToPounds"))(2,5)}")

Utilice  getConversionLambda  para  obtener  dos  lambdas.
//Definir  dos  lambdas  de  conversión

val  kgsToPoundsLambda  =  getConversionLambda("KgsToPounds")
val  librasToUSTonsLambda  =  getConversionLambda("LibrasToUSTons")
Cree  una  lambda  que  convierta  un  Doble  de  

//Combina  las  dos  lambdas  para  crear  una  nueva kilogramos  a  toneladas  estadounidenses.

val  kgsToUSTonsLambda  =  combine(kgsToPoundsLambda,  librasToUSTonsLambda)

//Utilice  la  nueva  lambda  para  convertir  17,4  a  toneladas  estadounidenses Utilice  la  lambda  para  convertir  17,4  

valor  valor  =  17.4 kilogramos  a  toneladas  estadounidenses.

println("$valor  kg  es  ${convert(valor,  kgsToUSTonsLambda)}  toneladas  estadounidenses")
}

Tomemos  el  código  para  una  prueba  de  manejo.

P:  He  oído  hablar  de  la  programación  funcional.  ¿Qué  es  eso?
Prueba  de  conducción

R:  Las  lambdas  son  una  parte  importante  del  funcionamiento
programación.  Mientras  que  la  programación  no  funcional  lee  la  
Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  
entrada  de  datos  y  genera  la  salida  de  datos,  los  programas  funcionales  
la  ventana  de  salida  del  IDE:
pueden  leer  funciones  como  entrada  y  generar  funciones  como  salida.  
Si  su  código  incluye  funciones  de  orden  superior,  está  haciendo  
Convertir  2.5kg  a  Libras:  5.5115575
programación  funcional.
17.4  se  convierte  a  0.0191802201

17,4  kg  son  0,0191802201  toneladas  estadounidenses P:  ¿ La  programación  funcional  es  muy  diferente  de
¿programación  orientada  a  objetos?

Ahora  ha  aprendido  a  usar  lambdas  para  crear  funciones  de  
orden  superior.  Pruebe  los  siguientes  ejercicios  y,  en  el  próximo   R:  Ambas  son  formas  de  factorizar  su  código.  en  objeto
capítulo,  le  presentaremos  algunas  de  las  funciones  integradas   la  programación  orientada  combina  datos  con  funciones,  y  en  
la  programación  funcional  combina  funciones  con  funciones.
de  orden  superior  de  Kotlin  y  le  mostraremos  cuán  poderosas  
Los  dos  estilos  de  programación  no  son  incompatibles;  son  
y  flexibles  pueden  ser.
simplemente  formas  diferentes  de  ver  el  mundo.

estas  aqui  4 355
Machine Translated by Google

código  imanes

Imanes  de  código
Alguien  usó  imanes  de  nevera  para  crear  una  función  de  búsqueda  que  imprima  los  
nombres  de  los  artículos  en  una  List<Grocery>  que  cumplen  con  algunos  criterios.  
La  función  va  aquí.
Desafortunadamente,  algunos  de  los  imanes  se  cayeron.  Vea  si  puede  reconstruir  la  función.

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
Esta  es  la  tienda  de  comestibles
clase  de  datos
unidad  val:  cadena,  unidad  valPrecio:  doble)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0),
La  función  
Comestibles  ("Hongos",  "Verdura",  "lb",  4.0),
principal  
utiliza  la   Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5),
función  de   Comestibles  ("Aceite  de  oliva",  "Despensa",  "Botella",  6.0),
búsqueda.
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0))
println("Ingredientes  caros:")
buscar  (comestibles)  {i:  Comestibles  ­>  i.unitPrice>  5.0}
println("Todas  las  verduras:")
buscar(comestibles)  {i:  Comestibles  ­>  i.categoría  ==  "Vegetal"}
println("Todos  los  paquetes:")
buscar(comestibles)  {i:  Comestibles  ­>  i.unidad  ==  "Paquete"}
}

println(l.nombre) yo  en  la  lista para  ( (g:  Supermercado)  ­>  Booleano


lista: , )

criterio(l) } buscar {
divertido } ) { (

Lista<comestibles> ) Respuestas  en  la  página  358.
si  ( criterios: } {

356  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

SER  el  compilador
Aquí  hay  cinco  funciones.  Tu  trabajo  es  jugar  
como  si  fueras  el  Compilador  y  determinar  si  
cada  uno  compilará.  Si  no  se  compila,  ¿por  

qué  no?

A fun  myFun1(x:  Int  =  6,  y:  (Int)  ­>  Int  =  7):  Int  { return  y(x)

B fun  myFun2(x:  Int  =  6,  y:  (Int)  ­>  Int  =  { it })  { return  y(x)

C fun  myFun3(x:  Int  =  6,  y:  (Int)  ­>  Int  =  { x:  Int  ­>  x  +  6 }):  Int  {
devolver  y(x)
}

D fun  myFun4(x:  Int,  y:  Int,
z:  (Int,  Int)  ­>  Int  =  {

x:  Int,  y:  Int  ­>  x  +  y
})  {

z(x,  y)
}

mi diversión  myFun5(x:  (Int)  ­>  Int  =  {
imprimir  (es)
es  +  7

})  {
x(4)
Respuestas  en  la  página  359.
}

estas  aqui  4 357
Machine Translated by Google

solución  de  imanes

Solución  de  imanes  de  código
Alguien  usó  imanes  de  nevera  para  crear  una  función  de  búsqueda  que  imprima  los  
nombres  de  los  artículos  en  una  List<Grocery>  que  cumplen  con  algunos  criterios.  
Desafortunadamente,  algunos  de  los  imanes  se  cayeron.  Vea  si  puede  reconstruir  la  función.

divertido buscar ( lista: Lista<comestibles> ,

criterios: (g:  Supermercado)  ­>  Booleano ) {

para  ( yo  en  la  lista ) {

si  ( criterio(l) ) { println(l.nombre) }

}
}

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
unidad  val:  cadena,  unidad  valPrecio:  doble)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0),
Comestibles  ("Hongos",  "Verdura",  "lb",  4.0),
Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5),
Comestibles  ("Aceite  de  oliva",  "Despensa",  "Botella",  6.0),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0))
println("Ingredientes  caros:")
buscar  (comestibles)  {i:  Comestibles  ­>  i.unitPrice>  5.0}
println("Todas  las  verduras:")
buscar(comestibles)  {i:  Comestibles  ­>  i.categoría  ==  "Vegetal"}
println("Todos  los  paquetes:")
buscar(comestibles)  {i:  Comestibles  ­>  i.unidad  ==  "Paquete"}
}

358  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

SER  la  solución  del  compilador
Aquí  hay  cinco  funciones.  Tu  
trabajo  es  jugar  como  si  fueras  el  
Compilador  y  determinar  si  cada  
uno  compilará.  Si  no  
se  compila,  ¿por  qué  
no?

A fun  myFun1(x:  Int  =  6,  y:  (Int)  ­>  Int  =  7):  Int  { return  y(x) Esto  no  se  compilará,  ya  que  asigna  un  
valor  Int  predeterminado  de  7  a  una  lambda.
}

Esto  no  se  compilará  porque  la  
B fun  myFun2(x:  Int  =  6,  y:  (Int)  ­>  Int  =  { it })  { return  y(x)
función  devuelve  un  Int  que  no  
Esta  línea  devuelve  un  Int.
} está  declarado.

C fun  myFun3(x:  Int  =  6,  y:  (Int)  ­>  Int  =  { x:  Int  ­>  x  +  6 }):  Int  {
devolver  y(x) Este  código  compila.  Sus  parámetros  tienen  valores  predeterminados  
}
del  tipo  correcto  y  su  tipo  de  retorno  está  declarado  correctamente.

D fun  myFun4(x:  Int,  y:  Int,
Este  código  compila.  A  la  
z:  (Int,  Int)  ­>  Int  =  {
variable  z  se  le  asigna  una  
x:  Int,  y:  Int  ­>  x  +  y
lambda  válida  como  su  valor  predeterminado.
})  {

z(x,  y)
}

mi diversión  myFun5(x:  (Int)  ­>  Int  =  { Este  código  compila.  A  la  

imprimir  (es) variable  x  se  le  asigna  una  
es  +  7 lambda  válida  como  su  valor  
})  {
predeterminado,  y  esta  lambda  abarca  varias  líneas.
x(4)
}

estas  aqui  4 359
Machine Translated by Google

solución  de  rompecabezas  de  piscina

Solución  de  rompecabezas  de  piscina
Su  trabajo  es  tomar  fragmentos  de  código  del  grupo  y  colocarlos  en  las  líneas  en  
blanco  del  código.  No  puede  usar  el  mismo  fragmento  de  código  más  de  una  
vez  y  no  necesitará  usar  todos  los  fragmentos  de  código.  Su  objetivo  es  crear  
una  función  nombrada  a  menos  que  sea  llamada  por  la  función  principal  a  
continuación.  La  función  a  menos  que  tenga  dos  parámetros,  una  condición  con  
nombre  booleano  y  un  código  con  nombre  lambda .  La  función  debe

invoque  el  código  lambda  cuando  la  condición  sea  falsa.

divertido  a  menos  que  ( condición:  booleano , código: ()  ­>  Unidad ) {

si  ( !condición ) {

código()
Si  la  condición  es  falsa,  invoque  el  código  lambda.
}

diversión  principal(argumentos:  Array<String>)  {
val  options  =  arrayOf("Rojo",  "Ambar",  "Verde")
var  crossWalk  =  opciones[(Math.random()  *  opciones.tamaño).toInt()]
if  (paso  de  peatones  ==  "Verde")  {

println("¡Camina!")
}

a  menos  que  (paso  de  peatones  ==  "Verde")  {

println("¡Alto!")
} Tiene  el  formato  de  un  bloque  de  código,  pero  en  realidad  
}
es  una  lambda.  La  lambda  se  pasa  a  la  función  a  menos  
que  se  ejecute  si  crossWalk  no  es  "Green".

No  necesitabas  usar  
estos  fragmentos.
Vacío

condición

Nulo

360  Capítulo  11
Machine Translated by Google

lambdas  y  funciones  de  orden  superior

Tu  caja  de  herramientas  de  Kotlin

CAPÍTULO  
11 Tiene  el  Capítulo  11  en  su  haber  y  ahora  ha  
agregado  lambdas  y  funciones  de  orden  superior  
a  su  caja  de  herramientas.
Puede  descargar  
el  código  
completo  del  
capítulo  desde  https://
tinyurl.com/HFKotlin.

Una  expresión  lambda,  o  lambda,  toma  la  forma: Ejecutas  una  lambda  invocándola.  Esto  se  hace  
pasando  a  la  lambda  cualquier  parámetro  entre  
paréntesis  o  llamando  a  su  función  de  invocación.
{ x:  Int  ­>  x  +  5 }

Puede  pasar  una  lambda  a  una  función  como  
La  lambda  se  define  entre  llaves  y  puede  incluir  
parámetro  o  usar  una  como  valor  de  retorno  de  una  
parámetros  y  un  cuerpo.
función.  Una  función  que  usa  una  lambda  de  esta  

Una  lambda  puede  tener  varias  líneas.  La  última   manera  se  conoce  como  función  de  orden  superior.
expresión  evaluada  en  el  cuerpo  se  usa  como  
Si  el  parámetro  final  de  una  función  es  una  
valor  de  retorno  de  la  lambda.
lambda,  puede  mover  la  lambda  fuera  de  los  
Puede  asignar  una  lambda  a  una  variable.  El  tipo   paréntesis  de  la  función  cuando  llame  a  la  función.
de  la  variable  debe  ser  compatible  con  el  tipo  de  
la  lambda.
Si  una  función  tiene  un  solo  parámetro  que  es  
El  tipo  de  una  lambda  tiene  el  formato: una  lambda,  puede  omitir  los  paréntesis  cuando  
llame  a  la  función.
(parámetros)  ­>  tipo_retorno
Un  alias  de  tipo  le  permite  proporcionar  un  
nombre  alternativo  para  un  tipo  existente.  Defina  
Siempre  que  sea  posible,  el  compilador  puede  inferir   un  alias  de  tipo  utilizando  typealias.
los  tipos  de  parámetros  de  la  lambda.

Si  la  lambda  tiene  un  solo  parámetro,  puede  
reemplazarlo  con  él.

estas  aqui  4 361
Machine Translated by Google
Machine Translated by Google

12 funciones  integradas  de  orden  superior

Fuerza Arriba Su Código

La  colección  se  estaba  volviendo  
loca,  había  artículos  por  todas  
partes,  así  que  le  di  un  mapa(),  le  
di  el  viejo  foldRight()  y  luego  ¡BAM!  
Todo  lo  que  quedó  fue  un  Int  de  42.

Kotlin  tiene  una  gran  cantidad  de  funciones  integradas  de  orden  superior.
Y  en  este  capítulo,  le  presentaremos  algunos  de  los  más  útiles.  Conocerá  a  la  familia  de  filtros  

flexibles  y  descubrirá  cómo  pueden  ayudarlo  a  reducir  su  colección  al  tamaño  adecuado.  Aprenderá  

cómo  transformar  una  colección  usando  el  mapa,  recorrer  sus  elementos  con  forEach  y  cómo  agrupar  

los  elementos  en  su  colección  usando  groupBy.  Incluso  usará  fold  para  realizar  cálculos  complejos  

usando  solo  una  línea  de  código.  Al  final  del  capítulo,  podrá  escribir  código  más  poderoso  de  lo  que  

nunca  pensó  posible.

este  es  un  nuevo  capitulo  363
Machine Translated by Google

clase  de  comestibles

Kotlin  tiene  un  montón  de  
funciones  integradas  de  orden  superior
Como  dijimos  al  comienzo  del  capítulo  anterior,  Kotlin  viene  con  un  
montón  de  funciones  integradas  de  orden  superior  que  toman  un  
parámetro  lambda,  muchas  de  las  cuales  se  ocupan  de  las  colecciones.
Le  permiten  filtrar  una  colección  en  función  de  algunos  criterios,  por  
ejemplo,  o  agrupar  los  elementos  de  una  colección  por  un  valor  de  
propiedad  particular.

Cada  función  de  orden  superior  tiene  una  implementación  generalizada  
y  su  comportamiento  específico  está  definido  por  la  lambda  que  le  
pasa.  Por  lo  tanto,  si  desea  filtrar  una  colección  con  la  función  de  filtro  
integrada,  puede  especificar  los  criterios  que  deben  usarse  pasando  a  
la  función  una  lambda  que  la  define.

Como  muchas  de  las  funciones  de  orden  superior  de  Kotlin  están  
diseñadas  para  funcionar  con  colecciones,  le  presentaremos  
algunas  de  las  funciones  de  orden  superior  más  útiles  definidas  en  
el  paquete  de  colecciones  de  Kotlin .  Exploraremos  estas  funciones  
usando  una  clase  de  datos  Grocery  y  una  Lista  de  artículos  de  
Grocery  llamados  groceries.  Aquí  está  el  código  para  definirlos:

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,

Esta  es  la  clase  de   val  unidad:  Cadena,  val  unidadPrecio:  Doble,
datos  Grocery. cantidad  de  valores:  Int)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0,  3),
Comestibles  ("Champiñones",  "Verduras",  "lb",  4.0,  1),
La  lista  de  comestibles  contiene  
Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5,  2),
cinco  artículos  de  comestibles.
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))
}

Comenzaremos  viendo  cómo  encontrar  el  valor  más  bajo  o  más  alto  en  
una  colección  de  objetos.

364  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Las  funciones  min  y  max  funcionan  con  tipos  básicos
Como  ya  sabe,  si  tiene  una  colección  de  tipos  básicos,  puede  
usar  las  funciones  min  y  max  para  encontrar  el  valor  más  bajo  
o  más  alto.  Si  desea  encontrar  el  valor  más  alto  en  List<Int>,  
por  ejemplo,  puede  usar  el  siguiente  código:

valores  enteros  =  listaDe(1,  2,  3,  4)

val  maxInt  =  ints.max() //maxInt  ==  4
Los  números  y  las  cadenas  
Las  funciones  min  y  max  funcionan  con  los  tipos  básicos  de   tienen  un  orden  natural,  lo  que  
Kotlin  porque  tienen  un  orden  natural.  Los  Ints  se  pueden   1,  2,  3,  4,  5... significa  que  puede  usar  las  
organizar  en  orden  numérico,  por  ejemplo,  lo  que  facilita  saber   funciones  min  y  max  con  ellos  
qué  Int  tiene  el  valor  más  alto,  y  las  cadenas  se  pueden   "A  B  C"... para  determinar  el  valor  más  bajo  
organizar  en  orden  alfabético. o  más  alto.

Sin  embargo,  las  funciones  min  y  max  no  se  pueden  usar  
con  tipos  sin  orden  natural.  No  puede  usarlos,  por  ejemplo,  con  
List<Grocery>  o  Set<Duck>,  ya  que  las  funciones  no  saben  
automáticamente  cómo  se  deben  ordenar  los  artículos  de  Grocery  
o  los  objetos  Duck.  Esto  significa  que  para  tipos  más  complejos,  
necesita  un  enfoque  diferente.

Las  funciones  minBy  y  maxBy  funcionan  con  TODOS  los  tipos
Estos  elementos  no  tienen  un  orden  natural.
Si  desea  encontrar  el  valor  más  bajo  o  más  alto  de  un  tipo  que  es  
Para  encontrar  el  valor  más  alto  o  más  bajo,  
más  complejo,  puede  usar  las  funciones  minBy  y  maxBy .
necesitamos  especificar  algunos  criterios,  
Estas  funciones  funcionan  de  manera  similar  a  min  y  max,  excepto  
como  el  precio  unitario  o  la  cantidad.
que  puede  pasarles  criterios.  Puede  usarlos,  por  ejemplo,  para  
encontrar  el  artículo  de  comestibles  con  el  precio  unitario  más  bajo  
o  el  pato  con  el  tamaño  más  grande.

Cada  una  de  las  funciones  minBy  y  maxBy  toma  un  parámetro:  
una  lambda  que  le  dice  a  la  función  qué  propiedad  debe  usar  
para  determinar  qué  elemento  tiene  el  valor  más  bajo  o  más  
alto.  Si,  por  ejemplo,  quisiera  encontrar  el  artículo  en  una  Lista  
<Comestibles>  con  el  precio  unitario  más  alto,  podría  hacerlo  
usando  la  función  maxBy  de  esta  manera: Este  código  es  como  decir  
"Encuentra  el  artículo  en  la  tienda  de  
val  precioUnitario  más  alto  =  comestibles.maxBy  { it.UnitPrice }
comestibles  con  el  precio  unitario  más  alto".

Y  si  quisiera  encontrar  el  artículo  con  el  valor  de  cantidad  más  bajo,  
usaría  minBy:
Esta  línea  devuelve  una  referencia  al  
val  cantidad  más  baja  =  comestibles.minBy  { it.quantity }
artículo  en  comestibles  con  la  cantidad  

La  expresión  lambda  que  pasa  a  la  función  minBy  o  maxBy  debe   más  baja.

tomar  una  forma  específica  para  que  el  código  se  compile  y  
funcione  correctamente.  Veremos  esto  a  continuación.

estas  aqui  4 365
Machine Translated by Google

minBy  y  maxBy

Una  mirada  más  cercana  al  parámetro  lambda  de  minBy  y  maxBy
Cuando  llama  a  la  función  minBy  o  maxBy,  debe  proporcionarle  una  lambda  que  
toma  la  siguiente  forma:

{ i:  tipo_elemento  ­>  criterios }

La  lambda  debe  tener  un  parámetro,  que  hemos  indicado  anteriormente  
usando  i:  item_type.  El  tipo  del  parámetro  debe  coincidir  con  el  tipo  de  
minBy  y  maxBy  
elemento  que  trata  la  colección,  por  lo  que  si  desea  utilizar  cualquiera  de  las  
funciones  con  List<Grocery>,  el  parámetro  de  la  lambda  debe  tener  un  tipo  de   funcionan  con  
Grocery:

{ i:  Supermercado  ­>  criterios } colecciones  que  
Como  cada  lambda  tiene  un  solo  parámetro  de  un  tipo  conocido,  podemos   contienen  cualquier  tipo  
omitir  la  declaración  del  parámetro  por  completo  y  referirnos  al  parámetro  
de  objeto,  lo  que  las  
en  el  cuerpo  de  lambda  usándolo.

El  cuerpo  lambda  especifica  los  criterios  que  deben  usarse  para   hace  mucho  más  flexibles  que  min  y
determinar  el  valor  más  bajo  o  más  alto  en  la  colección.
Este  criterio  suele  ser  el  nombre  de  una  propiedad,  por  ejemplo,  { it.unitPrice }.  
Puede  ser  de  cualquier  tipo,  siempre  y  cuando  la  función  pueda  usarlo  para  
determinar  qué  artículo  tiene  el  precio  más  bajo  o  el  más  bajo. Si  llama  a  minBy  o  
mayor  valor  de  la  propiedad.
maxBy  en  una  colección  
¿Qué  pasa  con  el  tipo  de  retorno  de  minBy  y  maxBy? que  no  contiene  
Cuando  llama  a  la  función  minBy  o  maxBy,  su  tipo  de  devolución  coincide  
elementos,  la  función  
con  el  tipo  de  los  elementos  que  se  encuentran  en  la  colección.  Si  usa  minBy  
con  List<Grocery>,  por  ejemplo,  la  función  devolverá  Grocery.  Y  si  usa  maxBy  
devolverá  un  valor  nulo.
con  un  Set<Duck>,  devolverá  un  Duck.

Ahora  que  sabe  cómo  usar  minBy  y  maxBy,  veamos  dos  de  sus  parientes  
cercanos:  sumBy  y  sumByDouble.

P:  Haz  las  funciones  min  y  max A:  trabajo  mínimo  y  máximo  con  tipos En  la  práctica,  min  y  max  funcionan  con  


¿Solo  funciona  con  los  tipos  básicos   donde  puede  comparar  dos  valores  y   cualquier  tipo  que  implemente  Comparable.
de  Kotlin,  como  números  y  cadenas? decir  si  un  valor  es  mayor  que  otro,  que   Sin  embargo,  en  lugar  de  implementar  
es  el  caso  de  los  tipos  básicos  de  Kotlin.   Comparable  en  sus  propias  clases,  creemos  
Estos  tipos  funcionan  de  esta  manera   que  usar  las  funciones  minBy  y  maxBy  es  
porque  detrás  de  escena,  cada  uno   un  mejor  enfoque,  ya  que  le  brindan  más  
implementa  la  interfaz  Comparable ,  que   flexibilidad.
define  cómo  se  deben  ordenar  y  comparar  
las  instancias  de  ese  tipo.

366  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Las  funciones  sumBy  y  sumByDouble
Como  es  de  esperar,  las  funciones  sumBy  y  sumByDouble  devuelven  
una  suma  de  los  elementos  de  una  colección  de  acuerdo  con  algunos   sumBy  suma  Ints  y  devuelve  
criterios  que  se  le  pasan  a  través  de  una  lambda.  Puede  usar  estas  
funciones  para,  por  ejemplo,  sumar  los  valores  de  cantidad  para  cada   un  Int.
artículo  en  List<Grocery>,  o  devolver  la  suma  de  cada  precio  de  
unidad  multiplicada  por  la  cantidad.

Las  funciones  sumBy  y  sumByDouble  son  casi  idénticas,  excepto  que  
sumBy  funciona  con  Ints  y  sumByDouble  funciona  con  Doubles.  Para  
sumByDouble  agrega  Doubles  
devolver  la  suma  de  los  valores  de  cantidad  de  una  tienda  de   y  devuelve  un  Double.
comestibles,  por  ejemplo,  usaría  la  función  sumBy,  ya  que  la  cantidad  
es  un  Int:
Esto  devuelve  la  suma  de  todos  los  
val  sumQuantity  =  comestibles.sumBy  { it.quantity }
valores  de  cantidad  en  comestibles.

Y  para  devolver  la  suma  de  cada  precio  unitario  multiplicado  por  
el  valor  de  la  cantidad,  usaría  sumByDouble,  ya  que  precio  
unitario  *  cantidad  es  un  doble:

val  PrecioTotal  =  comestibles.sumByDouble  { it.quantity  *  it.unitPrice }

parámetro  lambda  de  sumBy  y  sumByDouble
Al  igual  que  minBy  y  maxBy,  debe  proporcionar  sumBy  y  
sumByDouble  con  una  lambda  que  tome  esta  forma:

{ i:  tipo_elemento  ­>  criterios }

Como  antes,  item_type  debe  coincidir  con  el  tipo  de  elemento  que  
trata  la  colección.  En  los  ejemplos  anteriores,  estamos  usando  las  
funciones  con  List<Grocery>,  por  lo  que  el  parámetro  de  la  lambda   No  puede  usar  
debe  tener  un  tipo  de  Grocery.  Como  el  compilador  puede  inferir  esto,   sumBy  o  
podemos  omitir  la  declaración  del  parámetro  lambda  y  referirnos  al   sumByDouble  
parámetro  en  el  cuerpo  de  lambda  usándolo. directamente  en  un  
El  cuerpo  lambda  le  dice  a  la  función  lo  que  quieres  que  sume.  Como   mapa.
dijimos  anteriormente,  debe  ser  un  Int  si  está  usando  la  función  sumBy   Sin  embargo,  puede  usarlos  en  las  
y  un  Double  si  está  usando  sumByDouble.  sumBy  devuelve  un  valor  Int  y   claves,  valores  o  propiedades  de  
sumByDouble  devuelve  un  Double. las  entradas  de  un  mapa.  El  
Ahora  que  sabe  cómo  usar  minBy,  maxBy,  sumBy  y  sumByDouble,   siguiente  código,  por  ejemplo,  
creemos  un  nuevo  proyecto  y  agreguemos  código  que  use  estas   devuelve  la  suma  de  los  valores  
funciones. de  un  mapa:

myMap.values.sumBy  { it }

estas  aqui  4 367
Machine Translated by Google

crear  proyecto

Crear  el  proyecto  de  comestibles
Cree  un  nuevo  proyecto  de  Kotlin  que  se  dirija  a  la  JVM  y  asígnele  el  nombre  
"Comestibles".  Luego  cree  un  nuevo  archivo  de  Kotlin  llamado  Groceries.kt  resaltando  
la  carpeta  src ,  haciendo  clic  en  el  menú  Archivo  y  eligiendo  Nuevo  →  Archivo/clase  
de  Kotlin.  Cuando  se  le  solicite,  nombre  el  archivo  "Comestibles"  y  seleccione  Archivo  en  
la  opción  Tipo.

A  continuación,  actualice  su  versión  de  Groceries.kt  para  que  coincida  con  la  nuestra  a  continuación:

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
val  unidad:  Cadena,  val  unidadPrecio:  Doble,
cantidad  de  valores:  Int)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0,  3),
Comestibles  ("Champiñones",  "Verduras",  "lb",  4.0,  1),
Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5,  2),
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))

val  precioUnitario  más  alto  =  comestibles.maxBy  { it.UnitPrice  *  5 }
println("PrecioUnitario  mas  alto:  $PrecioUnitario  mas  alto") Comestibles
val  cantidad  más  baja  =  comestibles.minBy  { it.quantity }
println("Cantidad  más  baja:  $Cantidad  más  baja") origen

val  sumQuantity  =  comestibles.sumBy  { it.quantity } Comestibles.kt
println("cantidadsuma:  $cantidadsuma")
val  PrecioTotal  =  comestibles.sumByDouble  { it.quantity  *  it.unitPrice }
println("PrecioTotal:  $PrecioTotal")
}

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  salida  
del  IDE:

precioUnitario  más  alto:  Supermercado(nombre=Aceite  de  oliva,  categoría=Despensa,  unidad=Botella,  PrecioUnidad=6,0,  
cantidad=1)  Cantidad  más  baja:  Supermercado(nombre=Setas,  categoría=Vegetales,  unidad=lb,  Precio  unitario=4,0,  
cantidad=1)  sumaCantidad :  9  Precio  total:  28.0

368  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

SER  el  compilador
A  continuación  se  muestra  un  archivo  
fuente  completo  de  Kotlin.  Su  trabajo  es  
jugar  como  si  fuera  el  Compilador  y  
determinar  si  el  archivo  se  
compilará.  Si  no  se  
compila,  ¿por  qué  no?
¿Cómo  lo  corregirías?

pizza  de  clase  de  datos  (nombre  de  valor:  cadena,  precio  de  valor  por  rebanada:  doble,  cantidad  de  valor:  int)

diversión  principal(argumentos:  Array<String>)  {

valores  enteros  =  listaDe(1,  2,  3,  4,  5)

val  pizzas  =  listaDe(Pizza("Pollo  Soleado",  4.5,  4),

Pizza  ("Cabra  y  nuez",  4.0,  1),

Pizza  ("Tropical",  3.0,  2),

Pizza  ("El  jardín",  3.5,  3))

val  minInt  =  ints.minBy({ it.value })

val  minInt2  =  ints.minBy({ int:  Int  ­>  int })

val  sumInts  =  ints.sum()

val  sumInts2  =  ints.sumBy  { it }

val  sumInts3  =  ints.sumByDouble({ número:  Doble  ­>  número })

val  sumInts4  =  ints.sumByDouble  { int:  Int  ­>  int.toDouble() }

val  preciobajo  =  pizzas.min()

val  preciobajo2  =  pizzas.minBy({ it.pricePerSlice })

valcantidadalta  =  pizzas.maxBy  { p:  Pizza  ­>  p.cantidad }

valcantidadalta3  =  pizzas.maxBy  { it.quantity }

val  PrecioTotal  =  pizzas.sumBy  { it.pricePerSlice  *  it.quantity }

val  PrecioTotal2  =  pizzas.sumByDouble  { it.pricePerSlice  *  it.quantity }

estas  aqui  4 369
Machine Translated by Google

ser  la  solución  del  compilador

SER  la  solución  del  compilador
A  continuación  se  muestra  un  archivo  
fuente  completo  de  Kotlin.  Su  trabajo  es  
jugar  como  si  fuera  el  Compilador  y  
determinar  si  el  archivo  se  
compilará.  Si  no  se  
compila,  ¿por  qué  no?
¿Cómo  lo  corregirías?

pizza  de  clase  de  datos  (nombre  de  valor:  cadena,  precio  de  valor  por  rebanada:  doble,  cantidad  de  valor:  int)

diversión  principal(argumentos:  Array<String>)  {

valores  enteros  =  listaDe(1,  2,  3,  4,  5)

val  pizzas  =  listaDe(Pizza("Pollo  Soleado",  4.5,  4),

Pizza  ("Cabra  y  nuez",  4.0,  1),

Pizza  ("Tropical",  3.0,  2),

Pizza  ("El  jardín",  3.5,  3)) Como  ints  es  List<Int>,  'it'  es  Int  y  

no  tiene  propiedad  de  valor.
val  minInt  =  ints.minBy({ it.value })
Esta  línea  no  se  compilará,  ya  que  el  
val  minInt2  =  ints.minBy({ int:  Int  ­>  int })
parámetro  lambda  debe  ser  un  Int.  Podemos  
val  sumInts  =  ints.sum()
reemplazar  el  lambda  con  { it.toDouble() }.
val  sumInts2  =  ints.sumBy  { it }

val  sumInts3  =  ints.sumByDouble({ number:  Double  ­>  number  it.toDouble() })

val  sumInts4  =  ints.sumByDouble  { int:  Int  ­>  int.toDouble() }

La  función  min  no  funcionará  con  List<Pizza>.
val  preciobajo  =  pizzas.min()

val  preciobajo2  =  pizzas.minBy({ it.pricePerSlice })

valcantidadalta  =  pizzas.maxBy  { p:  Pizza  ­>  p.cantidad }

valcantidadalta3  =  pizzas.maxBy  { it.quantity }

val  PrecioTotal  =  pizzas.sumByDouble  { it.pricePerSlice  *  it.quantity }

val  PrecioTotal2  =  pizzas.sumByDouble  { it.pricePerSlice  *  it.quantity }

}
{ it.pricePerSlice  *  it.quantity }  devuelve  un  Double,  por  lo  que  la  función  
sumBy  no  funcionará.  Necesitamos  usar  sumByDouble  en  su  lugar.

370  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Conoce  la  función  de  filtro
La  siguiente  parada  en  nuestro  recorrido  por  las  funciones  de  orden  
superior  de  Kotlin  es  el  filtro.  Esta  función  le  permite  buscar  o  filtrar  una  
colección  de  acuerdo  con  algunos  criterios  que  le  pasa  mediante  una  lambda.

Para  la  mayoría  de  las  colecciones,  el  filtro  devuelve  una  Lista  que  
incluye  todos  los  elementos  que  coinciden  con  sus  criterios,  que  luego  
puede  usar  en  otra  parte  de  su  código.  Sin  embargo,  si  se  usa  con  un  
Esto  devuelve  una  Lista  que  contiene  
mapa,  devuelve  un  mapa.  El  siguiente  código,  por  ejemplo,  usa  la  
los  artículos  de  comestibles  cuyo  valor  
función  de  filtro  para  obtener  una  lista  de  todos  los  artículos  en  
de  categoría  es  "Vegetal".
comestibles  cuyo  valor  de  categoría  es  "Verdura":

val  verduras  =  groceries.filter  { it.category  ==  "Vegetal" }

Al  igual  que  las  otras  funciones  que  ha  visto  en  este  capítulo,  la  
lambda  que  pasa  a  la  función  de  filtro  toma  un  parámetro,  cuyo  tipo  
debe  coincidir  con  el  de  los  elementos  de  la  colección.  Como  el  
parámetro  de  lambda  tiene  un  tipo  conocido,  puede  omitir  la  declaración  
del  parámetro  y  hacer  referencia  a  él  en  el  cuerpo  de  lambda  usándolo.

El  cuerpo  de  la  lambda  debe  devolver  un  valor  booleano,  que  se  utiliza  
para  los  criterios  de  la  función  de  filtro.  La  función  devuelve  una  
referencia  a  todos  los  elementos  de  la  colección  original  donde  el  
cuerpo  lambda  se  evalúa  como  verdadero.  El  siguiente  código,  por  
ejemplo,  devuelve  una  lista  de  artículos  de  comestibles  cuyo  precio  
unitario  es  mayor  que  3.0:

val  unitPriceOver3  =  groceries.filter  { it.unitPrice  >  3.0 }

Hay  toda  una  FAMILIA  de  funciones  de  filtro
Puede  obtener  más  
Kotlin  tiene  varias  variaciones  de  la  función  de  filtro  que  a  veces  pueden   familia  
información  
de  filtros  
sobre  
de  la  
ser  útiles.  La  función  filterTo,  por  ejemplo,  funciona  como  la  función  de   Kotlin  en  la  
filtro,  excepto  que  agrega  los  elementos  que  coinciden  con  los  criterios  
documentación  en  línea:
especificados  a  otra  colección.
La  función  filterIsInstance  devuelve  una  lista  de  todos  los  elementos  
que  son  instancias  de  una  clase  dada.  Y  la  función  filterNot  devuelve  
los  elementos  de  una  colección  que  no  coinciden  con  los  criterios  que  le  
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.  colecciones/index.html

pasa.  Así  es  como,  por  ejemplo,  usaría  la  función  filterNot  para  devolver  
una  lista  de  todos  los  artículos  de  comestibles  cuyo  valor  de  categoría  no  
es  "Congelado":

val  notFrozen  =  comestibles.filterNot  { it.category  ==  "Frozen" }

Ahora  que  has  visto  cómo  funciona  la  función  de  filtro,  veamos  otra  de   filterNot  devuelve  aquellos  elementos  en  los  
las  funciones  de  orden  superior  de  Kotlin:  la  función  de  mapa.
que  el  cuerpo  lambda  se  evalúa  como  falso.

estas  aqui  4 371
Machine Translated by Google

función  de  mapa

Usa  el  mapa  para  
aplicar  una  transformación  a  tu  colección
La  función  map  toma  los  elementos  de  una  colección  y  transforma  cada  uno  de  
acuerdo  con  alguna  fórmula  que  especifique.  Devuelve  los  resultados  de  esta  
¡Sí!  La  función  de  mapa  devuelve  una  
transformación  como  una  nueva  Lista.
Lista,  y  no  un  Mapa.
Para  ver  cómo  funciona  esto,  suponga  que  tiene  una  List<Int>  que  se  ve  así:

valores  enteros  =  listaDe(1,  2,  3,  4)

Si  desea  crear  una  nueva  List<Int>  que  contenga  los  mismos  elementos  
multiplicados  por  dos,  puede  hacerlo  usando  la  función  de  mapa  de  esta  manera:

Esto  devuelve  una  lista  que  
val  doubleInts  =  ints.map  { it  *  2 } contiene  los  elementos  2,  4,  6  y  8.

Y  también  puede  usar  el  mapa  para  crear  una  nueva  lista  que  contenga  el  nombre  
de  cada  artículo  de  comestibles  en  comestibles:
Esto  crea  una  nueva  lista  y  la  

val  abarrotesNames  =  abarrotes.map  { it.name }
completa  con  el  nombre  de  cada  
artículo  de  comestibles  en  comestibles.
En  cada  caso,  la  función  map  devuelve  una  nueva  Lista  y  deja  intacta  la  
colección  original.  Si,  por  ejemplo,  usa  el  mapa  para  crear  una  lista  de  cada  
precio  unitario  multiplicado  por  0,5  usando  el  siguiente  código,  el  precio  unitario  
de  cada  artículo  de  comestibles  en  la  colección  original  permanece  igual:
Esto  devuelve  una  lista  

que  contiene  cada  precio  de  
val  medioPrecioUnitario  =  comestibles.map  { it.precioUnitario  *  0.5 }
unidad  multiplicado  por  0,5.
Al  igual  que  antes,  la  lambda  que  pasa  a  la  función  map  tiene  un  único  
parámetro  cuyo  tipo  coincide  con  el  de  los  elementos  de  la  colección.  
Puede  usar  este  parámetro  (generalmente  referido  a  su  uso)  para  especificar  cómo  
desea  que  se  transforme  cada  elemento  de  la  colección.

Puede  encadenar  llamadas  de  función  juntas
Como  las  funciones  de  filtro  y  mapa  devuelven  cada  una  una  colección,  
puede  encadenar  llamadas  a  funciones  de  orden  superior  para  realizar  
operaciones  más  complejas  de  manera  concisa.  Si  desea  crear  una  lista  de  
cada  precio  unitario  multiplicado  por  dos,  donde  el  precio  unitario  original  es  
mayor  que  3,0,  puede  hacerlo  llamando  primero  a  la  función  de  filtro  en  la  
colección  original  y  luego  usando  el  mapa  para  transformar  el  resultado:
Esto  llama  a  la  función  de  filtro  y  
val  newPrices  =  comestibles.filter  { it.unitPrice  >  3.0 }
luego  llama  al  mapa  en  la  Lista  
.map  { it.precioUnitario  *  2 } resultante.

Vayamos  detrás  de  escena  y  veamos  qué  sucede  cuando  se  ejecuta  este  código.

372  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Qué  sucede  cuando  se  ejecuta  el  código

1 val  newPrices  =  groceries.filter  { it.unitPrice  >  3.0 } .map  
{ it.unitPrice  *  2 }

La  función  de  filtro  se  llama  en  comestibles,  una  List<Grocery>.  Crea  una  nueva  lista  que  contiene  
referencias  a  aquellos  artículos  de  comestibles  cuyo  precio  unitario  es  mayor  que  3.0.

"Tomates"
La  llamada  a  la  función  de  filtro  
3.0
crea  una  nueva  Lista  que  contiene  
ÁRBITRO

0
Esta  es  la  lista  de   Tienda  de  comestibles
referencias  a  los  dos  elementos  con  
comestibles  original.
“Hongos”  4.0 un  precio  unitario  superior  a  3,0.
ÁRBITRO

ÁRBITRO Supermercado  
ÁRBITRO

ÁRBITRO
"Bagels"  1.5 0
2
comestibles

Tienda  de  comestibles ÁRBITRO
ÁRBITRO
valor
3 “Aceite  de   1
Lista<comestibles>
oliva”  6.0

Tienda  de  comestibles Lista<comestibles>
Lista<comestibles>

2 val  newPrices  =  groceries.filter  { it.unitPrice  >  3.0 } .map  
{ it.unitPrice  *  2 }

La  función  de  mapa  se  llama  en  la  nueva  Lista.  Como  la  lambda  { it.unitPrice  *  2 }  devuelve  un  
Double,  la  función  crea  una  List<Double>  que  contiene  una  referencia  a  cada  unitPrice  
multiplicada  por  2.
“Tomates”  3.0
8.0
ÁRBITRO

0
Tienda  de  comestibles
ÁRBITRO Doble
“Hongos”  4.0
ÁRBITRO 0
1 12.0

ÁRBITRO Supermercado   ÁRBITRO

ÁRBITRO
"Bagels"  1.5 1
Doble
2
comestibles

Tienda  de  comestibles
Lista<Doble>
ÁRBITRO
valor
3 “Aceite  de  
La  llamada  a  la  función  map  
Lista<comestibles>
oliva”  6.0
crea  una  nueva  Lista  que  
contiene  referencias  a  dos  Dobles.
Tienda  de  comestibles
Lista<comestibles>

estas  aqui  4 373
Machine Translated by Google

que  pasa

La  historia  continúa...
3 val  newPrices  =  groceries.filter  { it.unitPrice  >  3.0 } .map  
{ it.unitPrice  *  2 }

Se  crea  una  nueva  variable,  newPrices,  y  se  le  asigna  la  referencia  a  List<Double>  devuelta  
por  la  función  de  mapa.

“Tomates”  3.0

ÁRBITRO

8.0
0
Tienda  de  comestibles

“Hongos”  4.0
ÁRBITRO
ÁRBITRO Doble
ÁRBITRO
ÁRBITRO 1 0
nuevo 12.0
comestibles ÁRBITRO
Supermercado  "Bagels"   Precios ÁRBITRO

2 1.5
1
valor Doble
valor
Supermercado  
Lista<Doble>
Lista<comestibles> ÁRBITRO

3 “Aceite  de  oliva”   Lista<Doble>
6,0

La  Lista  creada  por  la  
Tienda  de  comestibles
Lista<comestibles> llamada  a  la  función  map  se  
asigna  a  la  variable  newPrices.

Ahora  que  ha  visto  lo  que  sucede  cuando  se  encadenan  
funciones  de  orden  superior,  echemos  un  vistazo  a  nuestra  
siguiente  función:  forEach.

P:  Dijiste  antes  que  la  función  de  filtro  tiene  un P:  Para  las  funciones  de  orden  superior  que  hemos  visto  hasta  ahora,
número  de  variaciones,  como  filterTo  y  filterNot. ha  dicho  que  el  tipo  de  parámetro  de  lambda  debe  coincidir  con  el  de  los  
elementos  de  la  colección.  ¿Cómo  se  aplica  eso?
¿Qué  pasa  con  el  mapa?  ¿Hay  variaciones  de  esa  función  también?

R:  ¡ Sí!  Las  variaciones  incluyen  mapTo  (que  agrega  los  resultados R:  Uso  de  genéricos.

de  la  transformación  a  una  colección  existente),  mapNotNull  (que  omite  
Como  recordará  del  Capítulo  10,  los  genéricos  le  permiten  escribir  código  
cualquier  valor  nulo)  y  mapValues  (que  trabaja  con  un  Map  y  lo  devuelve).  
que  usa  tipos  consistentemente.  Le  impide  agregar  una  referencia  de  
Puedes  encontrar  más  detalles  aquí:
Cabbage  a  List<Duck>.  Las  funciones  integradas  de  orden  superior  de  
Kotlin  usan  genéricos  para  asegurarse  de  que  solo  acepten  y  devuelvan  
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html
valores  cuyo  tipo  sea  apropiado  para  la  colección  con  la  que  se  están  
utilizando.

374  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

forEach  funciona  como  un  bucle  for
La  función  forEach  funciona  de  manera  similar  a  un  bucle  for,  ya  que  le  permite   Puede  usar  forEach  con  
realizar  una  o  más  acciones  en  cada  elemento  de  una  colección.  Estas  acciones  
se  especifican  mediante  una  lambda.
matrices,  listas,  conjuntos  
Para  ver  cómo  funciona  forEach,  suponga  que  desea  recorrer  cada  artículo  en  la  
Lista  de  compras  e  imprimir  el  nombre  de  cada  uno.  Así  es  como  podrías  hacer   y  en  las  propiedades  de  
esto  usando  un  bucle  for:
entradas,  claves  y  
para  (artículo  en  comestibles)  {
println(elemento.nombre) valores  de  un  mapa.
}

Y  aquí  está  el  código  equivalente  usando  la  función  forEach:

comestibles.forEach  { println(it.name) } Tenga  en  cuenta  que  { println(it.name) }  es  una  
lambda  que  estamos  pasando  a  la  función  forEach.
Ambos  ejemplos  de  código  hacen  lo  mismo,  pero  usar  forEach  es  un  
El  cuerpo  lambda  puede  tener  varias  líneas.
poco  más  conciso.

Pero  si  forEach  hace  lo  mismo  que  un  bucle  
for,  ¿no  me  está  dando  una  cosa  más  para  
recordar?  ¿ Cuál  es  el  punto  de  tener  otra  función?

Como  forEach  es  una  función,  puede  usarla  en  cadenas  de  llamadas  
de  función.

Imagine  que  desea  imprimir  el  nombre  de  cada  artículo  en  comestibles  cuyo  
precio  unitario  es  mayor  que  3.0.  Para  hacer  esto  usando  un  bucle  for,  podrías  
usar  el  código:

para  (artículo  en  comestibles)  {
if  (artículo.precio  unitario  >  3.0)  println(artículo.nombre)
}

Pero  puedes  hacer  esto  de  manera  más  concisa  usando:

comestibles.filter  { it.unitPrice  >  3.0 }
.forEach  { println(it.nombre) }

Por  lo  tanto,  forEach  le  permite  encadenar  llamadas  de  función  para  realizar  
tareas  poderosas  de  una  manera  concisa.

Echemos  un  vistazo  más  de  cerca  a  forEach.

estas  aqui  4 375
Machine Translated by Google

conseguir  el  cierre

forEach  no  tiene  valor  de  retorno
Al  igual  que  las  otras  funciones  que  ha  visto  en  este  capítulo,  la  lambda  
que  pasa  a  la  función  forEach  tiene  un  único  parámetro  cuyo  tipo  coincide  
con  el  de  los  elementos  de  la  colección.
Y  como  este  parámetro  tiene  un  tipo  conocido,  puede  omitir  la  
declaración  del  parámetro  y  hacer  referencia  al  parámetro  en  el  cuerpo  
lambda  usándolo.

Sin  embargo,  a  diferencia  de  otras  funciones,  el  cuerpo  de  la  lambda  tiene  
un  valor  de  retorno  de  unidad.  Esto  significa  que  no  puede  usar  forEach  
para  devolver  el  resultado  de  algún  cálculo,  ya  que  no  podrá  acceder  a  él.
Sin  embargo,  hay  una  solución.

Las  lambdas  tienen  acceso  a  las  variables.

Como  ya  sabes,  el  cuerpo  de  un  bucle  for  tiene  acceso  a  las  variables  
que  se  han  definido  fuera  del  bucle.  El  siguiente  código,  por  ejemplo,  
define  una  variable  de  cadena  denominada  itemNames,  que  luego  se  
actualiza  en  el  cuerpo  de  un  bucle  for:

var  nombres  de  elementos  = ""

para  (artículo  en  comestibles)  {
nombres  de  elementos  +=  "${elemento.nombre}  "
Puede  actualizar  la  variable  itemNames  
} dentro  del  cuerpo  de  un  bucle  for.
println("Nombres  de  elementos:  $  Nombres  de  elementos")

Cuando  pasa  una  lambda  a  una  función  de  orden  superior  como  
forEach,  la  lambda  tiene  acceso  a  estas  mismas  variables,  aunque  se  
hayan  definido  fuera  de  la  lambda.  Esto  significa  que  en  lugar  de  usar  el  
valor  de  retorno  de  la  función  forEach  para  obtener  el  resultado  de  algún  
cálculo,  puede  actualizar  una  variable  desde  el  interior  del  cuerpo  lambda.  
También  puede  actualizar  la  variable  
El  siguiente  código,  por  ejemplo,  es  válido: itemNames  dentro  del  cuerpo  de  la  
""
lambda  que  se  pasa  a  forEach.
var  nombres  de  elementos  =

comestibles.forEach({ itemNames  +=  "${it.name}  " })
println("Nombres  de  elementos:  $  Nombres  de  elementos")

Las  variables  definidas  fuera  de  lambda  a  las  que  puede  acceder  lambda   El  cierre  significa  que  
a  veces  se  denominan  cierre  de  lambda.  En  palabras  ingeniosas,  decimos  que  
la  lambda  puede  acceder  a  su  cierre.  Y  como  la  lambda  usa  la  variable   una  lambda  puede  
itemNames  en  su  cuerpo,  decimos  que  el  cierre  de  la  lambda  ha  capturado  la  
variable. acceder  a  cualquier  
Ahora  que  ha  aprendido  a  usar  la  función  forEach,  actualicemos  el  código  
de  nuestro  proyecto.
variable  local  que  capture.

376  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Actualizar  el  proyecto  de  comestibles

Agregaremos  algo  de  código  a  nuestro  proyecto  Groceries  que  usa  las  funciones  
filter,  map  y  forEach.  Actualice  su  versión  de  Groceries.kt  en  el  proyecto  para  
que  coincida  con  la  nuestra  a  continuación  (nuestros  cambios  están  en  negrita):

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
val  unidad:  Cadena,  val  unidadPrecio:  Doble,
cantidad  de  valores:  Int)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listOf(Supermercado("Tomates",  "Vegetales",  "lb",  3.0,  3),  Supermercado("Setas",  
"Vegetales",  "lb",  4.0,  1),  Supermercado("Bagels",  "  Panadería",  "Paquete",  
1.5,  2),
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))

val  precioUnitario  más  alto  =  comestibles.maxBy  { it.UnitPrice  *  5 }
println("PrecioUnitario  mas  alto:  $PrecioUnitario  mas  alto") Comestibles

val  cantidad  más  baja  =  comestibles.minBy  { it.quantity }
Borra   println("Cantidad  más  baja:  $Cantidad  más  baja") origen

estas  
líneas.
val  sumaCantidad  =  comestibles.sumBy  { it.cantidad }  println("sumaCantidad:   Comestibles.kt

$sumaCantidad")
val  PrecioTotal  =  comestibles.sumByDouble  { it.quantity  *  it.unitPrice }
println("PrecioTotal:  $PrecioTotal")
Añade  todas  estas  líneas.

val  verduras  =  groceries.filter  { it.category  ==  "Vegetal" }
println("verduras:  $verduras")
val  noFrozen  =  comestibles.filterNot  { it.category  ==  "Frozen" }  println("notFrozen:  $notFrozen")

val  nombres  de  comestibles  =  comestibles.map  { it.name }  println  
("nombres  de  comestibles:  $  nombres  de  comestibles")  val  
halfUnitPrice  =  comestibles.map  { it.unitPrice  *  0.5 }  println  ("mitad  de  precio  unitario:  $  
mitad  de  precio  unitario")

val  newPrices  =  comestibles.filter  { it.unitPrice  >  3.0 }
.map  { it.precioUnitario  *  2 } El  código  continúa  en  la  
println("nuevosPrecios:  $nuevosPrecios") página  siguiente.

estas  aqui  4 377
Machine Translated by Google

prueba  de  manejo

El  código  continuó... Agregue  estas  líneas  a  la  función  principal.

println("Nombres  de  las  tiendas  de  comestibles:  ")

comestibles.forEach  { println(it.name) }

println("Comestibles  con  precio  unitario  >  3.0:  ")
comestibles.filter  { it.unitPrice  >  3.0 }
Comestibles
.forEach  { println(it.nombre) }

var  nombres  de  elementos  = ""
origen

comestibles.forEach({ itemNames  +=  "${it.name}  " })  println("itemNames:  $itemNames")
Comestibles.kt

Tomemos  el  código  para  una  prueba  de  manejo.

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  salida  del  IDE:

verduras:  [Supermercado  (nombre=Tomates,  categoría=Verdura,  unidad=lb,  precio  unitario=3,0,  cantidad=3),  
Supermercado(nombre=champiñones,  categoría=Verdura,  unidad=lb,  precio  unitario=4,0,  cantidad=1)]  no  congeladas :  
[Supermercado(nombre=Tomates,  categoría=Vegetales,  unidad=lb,  precio  unitario=3.0,  cantidad=3),  Supermercado(nombre=Setas,  
categoría=Verduras,  unidad=lb,  precio  unitario=4.0,  cantidad=1),  Supermercado( name=Bagels,  categoría=Panadería,  
unidad=Paquete,  precio  unitario=1.5,  cantidad=2),  Supermercado(nombre=Aceite  de  oliva,  categoría=Despensa,  unidad=Botella,  
Precio  unitario=6.0,  cantidad=1)]  Champiñones,  Bagels,  Aceite  de  oliva,  Helado]  HalfUnitPrice:  [1.5,  2.0,  0.75,  3.0,  1.5]  newPrices:  
[8.0,  12.0]

Nombres  de  comestibles:
Tomates
Hongos

Bagels
Aceite  de  oliva

Helado
Comestibles  con  precio  unitario  >  3.0:
Hongos
Aceite  de  oliva

Nombres  de  los  artículos:  Tomates  Champiñones  Bagels  Aceite  de  oliva  Helado

Ahora  que  ha  actualizado  el  código  de  su  proyecto,  realice  el  siguiente  ejercicio  y  luego  veremos  
nuestra  próxima  función  de  orden  superior.

378  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Rompecabezas  de  piscina

Su  trabajo  es  tomar  fragmentos  de  código  del  
clase  abstracta  Mascota  (var  nombre:  Cadena)
grupo  y  colocarlos  en  las  líneas  en  blanco  
del  código.  No  puede  usar  el  mismo  
clase  Gato(nombre:  Cadena) :  Mascota(nombre) fragmento  de  código  más  de  una  vez  y  no  
necesitará  usar  todos  los  fragmentos  de  
código.  Su  objetivo  es  completar  la  
clase  Perro(nombre:  Cadena) :  Mascota(nombre) función  getWinners
en  la  clase  Contest  para  que  vuelva
un  Set<T>  de  concursantes  con  la  puntuación  más  
clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)
alta  e  imprime  el  nombre  de  cada  ganador.

clase  Concurso<T:  Mascota>()  {

var  puntuaciones:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {
Si  este  código  le  resulta  familiar,  
if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)
es  porque  escribimos  una  
} versión  diferente  en  el  Capítulo  10.

diversión  obtenerGanadores():  Establecer<T>  {

val  puntuación  más  alta  =
val  ganadores  =  puntajes { ==  puntuación  más  alta }

ganadores { println("Ganador:  ${ }") }


ganadores  de  regreso

Nota:  ¡cada  cosa  del  grupo  
solo  se  puede  usar  una  vez!

puntuaciones
mapa

valores filtrar
llaves
máx()
valores para  cada
. . .
nombre
valor maxBy()
. . . .
él él

estas  aqui  4 379
Machine Translated by Google

solución  de  rompecabezas  de  piscina

Solución  de  rompecabezas  de  piscina
Su  trabajo  es  tomar  fragmentos  de  código  del  grupo  y  
clase  abstracta  Mascota  (var  nombre:  Cadena)
colocarlos  en  las  líneas  en  blanco  del  código.  No  
puede  usar  el  mismo  fragmento  de  código  más  

clase  Gato(nombre:  Cadena) :  Mascota(nombre) de  una  vez  y  no  necesitará  usar  todos  los  
fragmentos  de  código.  Su  objetivo  es  completar  
la  función  getWinners
clase  Perro(nombre:  Cadena) :  Mascota(nombre)
en  la  clase  Contest  para  que  devuelva  un  Set<T>  

de  concursantes  con  la  puntuación  más  alta  e  imprima  el  
clase  Pez  (nombre:  Cadena) :  Mascota  (nombre)
nombre  de  cada  ganador.

clase  Concurso<T:  Mascota>()  {

var  puntuaciones:  MutableMap<T,  Int>  =  mutableMapOf()

diversión  addScore(t:  T,  puntuación:  Int  =  0)  {

if  (puntuación  >=  0)  puntuaciones.put(t,  puntuación)

} Las  puntuaciones  se  mantienen  como  valores  Int  en  un  MutableMap  
llamado  puntuaciones,  por  lo  que  obtiene  el  valor  de  puntuación  más  alto.

diversión  obtenerGanadores():  Establecer<T>  { Filtre  las  puntuaciones  para  
obtener  las  entradas  cuyo  
val  puntuación  más  alta  = puntuaciones.valores.max()
valor  sea  HighScore.
val  ganadores  =  puntajes .filtrar { es.valor ==  puntuación  más  alta } .keys  
Luego  use  su  propiedad  
ganadores .para  cada { println("Ganador:  ${ }") } it.name  
de  claves  para  obtener  
ganadores  de  regreso los  ganadores.
Utilice  la  función  forEach  para  imprimir  
} el  nombre  de  cada  ganador.

No  necesitabas  usar  estos  

fragmentos.

mapa

valores

maxBy()

380  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Use  groupBy  para  dividir  su  colección  en  grupos
La  siguiente  función  que  veremos  es  groupBy.  Esta  función  te  permite  
Tenga  en  cuenta  que  no  puede  usar  groupBy  en  
agrupar  los  elementos  de  tu  colección  según  algunos  criterios,  como  el  
un  mapa  directamente,  pero  puede  llamarlo  en  sus  
valor  de  una  de  sus  propiedades.  Puede  usarlo  (junto  con  otras  llamadas  
claves,  valores  o  propiedades  de  entrada.
a  funciones)  para,  por  ejemplo,  imprimir  el  nombre  de  los  artículos  de  
Supermercado  agrupados  por  valor  de  categoría:

Verdura
Tomates
Hongos
Estos  son  los  
Panadería La  tienda  de  comestibles
valores  de  categoría.
Bagels Los  Nombres  son

Despensa agrupados  por  
Aceite  de  oliva
valor  de  categoría.
Congelado
Helado

La  función  groupBy  acepta  un  parámetro,  un  lambda,  que  se  usa  para  
especificar  cómo  la  función  debe  agrupar  los  elementos  de  la  colección.  
El  siguiente  código,  por  ejemplo,  agrupa  los  artículos  en  comestibles  (una  
Lista<Comestibles>)  por  el  valor  de  categoría:
Esto  es  como  decir  "agrupar  cada  
val  groupByCategory  =  comestibles.groupBy  { it.category } artículo  en  comestibles  por  su  
valor  de  categoría".
groupBy  devuelve  un  mapa.  Utiliza  los  criterios  pasados  a  través  
del  cuerpo  lambda  para  las  claves,  y  cada  valor  asociado  es  una  lista  de  
elementos  de  la  colección  original.  El  código  anterior,  por  ejemplo,  crea  un  
Mapa  cuyas  claves  son  los  valores  de  la  categoría  de  artículos  de  
Supermercado,  y  cada  valor  es  una  List<Supermercado>:

Bagels Aceituna
Aceite
Tomates

Tienda  de  comestibles Tienda  de  comestibles

Tienda  de  comestibles
ÁRBITRO
Hielo
0 Hongos
ÁRBITRO ÁRBITRO Crema
0 0
Tienda  de  comestibles
ÁRBITRO

Tienda  de  comestibles
1
Lista<comestibles> Lista<comestibles>
ÁRBITRO

0
Lista<comestibles>
ÁRBITRO ÁRBITRO ÁRBITRO ÁRBITRO

Lista<comestibles>
Cada  valor  en  el  

Mapa  es  una  
Los  valores  de  categoría  se  
Lista<Comestibles>. "Verdura" "Panadería" "Despensa" "Congelado"
utilizan  para  las  claves  del  mapa,  

por  lo  que  cada  clave  es  una  cadena.
Mapa<Cadena,  Lista<Comestibles>>

estas  aqui  4 381
Machine Translated by Google

cadenas  de  llamadas

Puede  usar  groupBy  en  cadenas  de  llamadas  de  función
Como  la  función  groupBy  devuelve  un  mapa  con  valores  de  lista,  puede  realizar  
más  llamadas  a  funciones  de  orden  superior  en  su  valor  devuelto,  tal  como  
puede  hacerlo  con  las  funciones  de  filtro  y  mapa.

Imagine  que  desea  imprimir  el  valor  de  cada  categoría  para  List<Grocery>,  
junto  con  el  nombre  de  cada  artículo  de  Grocery  cuya  propiedad  de  categoría  
tiene  ese  valor.  Para  hacer  esto,  puede  usar  la  función  groupBy  para  agrupar  
los  artículos  de  Supermercado  por  cada  valor  de  categoría  y  luego  usar  la  
función  forEach  para  recorrer  el  mapa  resultante:
groupBy  devuelve  un  Map,  lo  que  significa  
que  podemos  llamar  a  la  función  forEach  en  su  
comestibles.groupBy  { it.category }.forEach  {
valor  de  retorno.
//Más  código  va  aquí

Como  la  función  groupBy  usa  los  valores  de  la  categoría  Grocery  para  sus  
claves,  podemos  imprimirlos  pasando  el  código  println(it.key)  a  la  función  
forEach  en  su  lambda:

comestibles.groupBy  { it.category }.forEach  {

println(it.clave)
Esto  imprime  las  claves  del  mapa  (los  
//Más  código  va  aquí valores  de  la  categoría  de  comestibles).

Y  como  cada  uno  de  los  valores  del  Mapa  es  una  List<Grocery>,  podemos  
hacer  una  llamada  adicional  a  forEach  para  imprimir  el  nombre  de  cada  
artículo  de  la  tienda:

comestibles.groupBy  { it.category }.forEach  {

println(it.clave) Esta  línea  obtiene  el  valor  
correspondiente  a  la  clave  del  Mapa.  
it.value.forEach  { println(" ${it.nombre}") }
Como  se  trata  de  una  List<Grocery>,  
} podemos  llamar  a  forEach  para  
Entonces,  cuando  ejecuta  el  código  anterior,  produce  el  siguiente  resultado: imprimir  el  nombre  del  artículo  de  Grocery.

Verdura
Tomates
Hongos
Panadería
Bagels
Despensa
Aceite  de  oliva

Congelado
Helado

Ahora  que  sabe  cómo  usar  groupBy,  veamos  la  función  final  de  nuestro  
viaje  por  carretera:  la  función  de  plegado.

382  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Cómo  utilizar  la  función  de  plegado

Podría  decirse  que  la  función  de  pliegue  es  la  función  de  orden  superior  más  flexible   fold  se  puede  llamar  en  
de  Kotlin.  Con  fold,  puede  especificar  un  valor  inicial  y  realizar  alguna  operación  en  
él  para  cada  elemento  de  una  colección.  Puede  usarlo  para,  por  ejemplo,  multiplicar  todos   las  claves,  valores  y  
los  elementos  de  List<Int>  y  devolver  el  resultado,  o  concatenar  el  nombre  de  cada  
elemento  de  List<Grocery>,  todo  en  una  sola  línea  de  código.
propiedades  de  las  

entradas  de  un  mapa,  
A  diferencia  de  las  otras  funciones  que  hemos  visto  en  este  capítulo,  fold  toma  dos  
parámetros:  el  valor  inicial  y  la  operación  que  desea  realizar  en  él,  especificada  por  una  
pero  no  en  un  mapa  directamente.
lambda.  Entonces,  si  tiene  la  siguiente  List<Int>:

valores  enteros  =  listaDe(1,  2,  3)

puede  usar  fold  para  agregar  cada  uno  de  sus  elementos  a  un  valor  inicial  de  0  usando  el  
siguiente  código:

val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento}

Este  es  el  valor  inicial.
Esto  le  dice  a  la  función  que  desea  
El  primer  parámetro  de  la  función  de  plegado  es  el  valor  inicial,  en  este  caso,  0.
agregar  el  valor  de  cada  elemento  de  la  
Este  parámetro  puede  ser  de  cualquier  tipo,  pero  generalmente  es  uno  de  los  tipos  
colección  al  valor  inicial.
básicos  de  Kotlin,  como  un  número  o  una  cadena.

El  segundo  parámetro  es  una  lambda  que  describe  la  operación  que  desea  realizar  en  el  
valor  inicial  de  cada  elemento  de  la  colección.
En  el  ejemplo  anterior,  queremos  agregar  cada  elemento  al  valor  inicial,  por  lo  que  
estamos  usando  la  lambda: Aquí,  hemos  decidido  nombrar  los  parámetros  lambda  
runningSum  y  item  ya  que  estamos  sumando  el  valor  de  cada  
{ suma  en  ejecución,  elemento  ­>  suma  en  ejecución  +  elemento }
elemento  a  una  suma  acumulada.  Sin  embargo,  puede  dar  a  
los  parámetros  cualquier  nombre  de  variable  válido.
La  lambda  que  pasa  para  doblar  tiene  dos  parámetros,  que  en  este  ejemplo  hemos  
llamado  runningSum  y  item.

El  primer  parámetro  lambda,  runningSum,  obtiene  su  tipo  del  valor  inicial  que  especifique.  
Se  inicializa  con  este  valor  inicial,  por  lo  que  en  el  ejemplo  anterior,  runningSum  es  un  Int  
que  se  inicializa  con  0.

El  segundo  parámetro  lambda,  item,  tiene  el  mismo  tipo  que  los  elementos  de  la  
colección.  En  el  ejemplo  anterior,  llamamos  a  fold  en  List<Int>,  por  lo  que  el  tipo  de  
elemento  es  Int.

El  cuerpo  de  lambda  especifica  la  operación  que  desea  realizar  para  cada  elemento  
de  la  colección,  cuyo  resultado  se  asigna  luego  a  la  primera  variable  de  parámetro  de  
lambda.  En  el  ejemplo  anterior,  la  función  toma  el  valor  de  runningSum,  lo  suma  al  valor  
del  elemento  actual  y  asigna  este  nuevo  valor  a  runningSum.  Cuando  la  función  ha  
recorrido  todos  los  elementos  de  la  colección,  fold  devuelve  el  valor  final  de  esta  variable.

Analicemos  lo  que  sucede  cuando  llamamos  a  la  función  de  plegado.
estas  aqui  4 383
Machine Translated by Google

que  pasa

Detrás  de  escena:  la  función  de  plegado

Esto  es  lo  que  sucede  cuando  ejecutamos  el  código:

val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento}

donde  ints  se  define  como:

valores  enteros  =  listaDe(1,  2,  3)

1 val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento}

Esto  crea  una  variable  Int  llamada  runningSum  que  se  inicializa  con  0.  Esta  variable  es  
local  para  la  función  de  plegado.

0
Este  es  el  valor  inicial  que  le  hemos  pasado  a  la  
ÁRBITRO
función  de  plegado.  Se  asigna  a  una  variable  local  
llamada  runningSum.
En  t
correr
Suma

var  Int

2 val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento }

La  función  toma  el  valor  del  primer  elemento  de  la  colección  (un  Int  con  un  valor  de  1)  
y  lo  agrega  al  valor  de  runningSum.  Este  nuevo  valor,  1,  se  asigna  a  runningSum.

1
La  función  
ÁRBITRO

de  plegado  
0 1
comienza   En  t
con  el   ÁRBITRO

ÁRBITRO 2
primer  
En  t
elemento  de   1 correr
Suma El  valor  del  primer  elemento  
la  colección. En  t
ÁRBITRO
se  suma  al  valor  de  
3 var  Int
2 runningSum.
Luego,  este  valor  se  
En  t asigna  a  runningSum.
lista<int>

384  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

La  historia  continúa...
3 val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento }

La  función  se  mueve  al  segundo  elemento  de  la  colección,  que  es  un  Int  con  un  valor  de  2.  
Lo  agrega  a  runningSum,  de  modo  que  el  valor  de  runningSum  se  convierte  en  3.

1
ÁRBITRO

La  función   0 3
de  plegado   En  t
ÁRBITRO
se  mueve   ÁRBITRO 2
al  segundo   En  t
1 correr
elemento  de   Suma La  función  suma  el  valor  del  
la  colección. En  t
ÁRBITRO
segundo  elemento  al  valor  de  
3 var  Int
2 runningSum.
Este  nuevo  valor  se  asigna  a  

En  t runningSum.
lista<int>

4 val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento }

La  función  pasa  al  tercer  y  último  elemento  de  la  colección:  un  Int  con  un  valor  de  3.  Este  
valor  se  agrega  a  runningSum,  de  modo  que  el  valor  de  runningSum  se  convierte  en  6.

1
ÁRBITRO

0 6
En  t
ÁRBITRO
Finalmente,   ÁRBITRO 2
la  función   En  t
1 correr
de  plegado   Suma
En  t La  función  agrega  el  
se  mueve  
valor  del  elemento  final  al  
ÁRBITRO

al  tercer   3 var  Int
2 valor  de  runningSum.  El  
elemento  de  la  colección.
nuevo  valor  de  runningSum  
En  t ahora  es  6
lista<int>

5 val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento}

Como  no  hay  más  elementos  en  la  colección,  la  función  devuelve  el  valor  final  de  
runningSum.  Este  valor  se  asigna  a  una  nueva  variable  llamada  sumOfInts.

ÁRBITRO
6 El  valor  final  de  runningSum  es  6,  por  
suma lo  que  la  función  devuelve  este  valor.
OfInts Se  asigna  a  sumOfInts.
En  t
var  Int

estas  aqui  4 385
Machine Translated by Google

función  de  plegado

Algunos  ejemplos  más  de  pliegue
Ahora  que  ha  visto  cómo  usar  la  función  de  plegado  para  
sumar  los  valores  en  List<Int>,  veamos  algunos  ejemplos  más.

Encuentra  el  producto  de  una  List<Int>

Si  desea  multiplicar  todos  los  números  en  List<Int>  y  devolver  el  
resultado,  puede  hacerlo  pasando  a  la  función  de  pliegue  un  valor  
inicial  de  1  y  una  lambda  cuyo  cuerpo  realiza  la  multiplicación:

ints.fold(1)  { producto  en  ejecución,  elemento  ­>  producto  en  ejecución  *  elemento }

Multiplique  runningSum  por  el  valor  de  cada  elemento.
Inicialice  runningProduct  con  1.

Concatenar  el  nombre  de  cada  artículo  en  una  Lista  <Comestibles>

Para  devolver  un  String  que  contiene  el  nombre  de  cada  artículo  
También  hay  una  función  joinToString  que  
de  Grocery  en  List<Grocery>,  puede  pasar  a  la  función  de  plegado  
puede  usar  para  realizar  este  tipo  de  tareas.
un  valor  inicial  de  ""  y  una  lambda  cuyo  cuerpo  realiza  la  
concatenación:

comestibles.fold("")  { cadena,  artículo  ­>  cadena  + "  ${elemento.nombre}" }

Inicialice  la  cadena  con  "". Esto  es  como  decir:  
cadena  =  cadena  +  p“  ara  
${item.name}”  
cada  artículo  
en  comestibles.
Reste  el  precio  total  de  los  artículos  de  un  valor  inicial

También  puede  usar  fold  para  calcular  cuánto  cambio  le  quedaría  si  
comprara  todos  los  artículos  en  List<Grocery>.  Para  hacer  esto,  
establecería  el  valor  inicial  como  la  cantidad  de  dinero  que  tiene  
disponible  y  usaría  el  cuerpo  lambda  para  restar  el  precio  unitario  de  
cada  artículo  multiplicado  por  la  cantidad:

comestibles.fold(50.0)  { cambio,  artículo

­>  cambiar  ­  item.unitPrice  *  item.quantity }
Inicialice  el  cambio  con  50.0.
Esto  resta  el  precio  total  (precio  
unitario  *  cantidad)  del  cambio  de  
Ahora  que  sabe  cómo  usar  las  funciones  groupBy  y  fold,   cada  artículo  en  comestibles.
actualicemos  el  código  de  nuestro  proyecto.

386  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

Actualizar  el  proyecto  de  comestibles

Agregaremos  algo  de  código  a  nuestro  proyecto  Groceries  que  usa  las  funciones  
groupBy  y  fold.  Actualice  su  versión  de  Groceries.kt  en  el  proyecto  para  que  coincida  
con  la  nuestra  a  continuación  (nuestros  cambios  están  en  negrita):

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
val  unit:  Cadena,  val  unitPrice:  Doble,  val  cantidad:  Int)

diversión  principal(argumentos:  Array<String>)  {
val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0,  3),
Comestibles  ("Champiñones",  "Verduras",  "lb",  4,0,  1),  Comestibles  
("Bagels",  "Panadería",  "Paquete",  1,5,  2),
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))

val  verduras  =  comestibles.filter  { it.category  ==  "Verdura" }  println("verduras:  $verduras")

val  notFrozen  =  comestibles.filterNot  { it.category  ==  "Frozen" }
println("nocongelado:  $nocongelado")

val  nombresdecomestibles  =  comestibles.map  { it.name }  
println("nombresdecomestibles:  $nombresdecomestibles")
val  medioPrecioUnitario  =  comestibles.map  { it.precioUnitario  *  0.5 }
Nosotros  no println("mitadPrecioUnitario:  $mitadPrecioUnitario")
Ya  no  necesita  
estas  líneas,  por  
val  newPrices  =  comestibles.filter  { it.unitPrice  >  3.0 }
lo  que  puede   .map  { it.precioUnitario  *  2 }
eliminarlas.
println("nuevosPrecios:  $nuevosPrecios")

Comestibles
println("Nombres  de  las  tiendas  de  comestibles:  ")

comestibles.forEach  { println(it.name) }
origen

println("Comestibles  con  precio  unitario  >  3.0:  ")
Comestibles.kt
comestibles.filter  { it.unitPrice  >  3.0 }
.forEach  { println(it.nombre) }

var  nombres  de  elementos  = ""

comestibles.forEach({ itemNames  +=  "${it.name}  " }) El  código  continúa  en  
println("Nombres  de  elementos:  $  Nombres  de  elementos") la  página  siguiente.

estas  aqui  4 387
Machine Translated by Google

prueba  de  manejo

El  código  continuó...
Comestibles
comestibles.groupBy  { it.category }.forEach  {
println(it.clave)
origen
it.value.forEach  { println(" ${it.nombre}") }
}

Comestibles.kt
valores  enteros  =  listaDe(1,  2,  3)
val  sumOfInts  =  ints.fold(0)  { sumaAcumulativa,  elemento  ­>  sumaAcumulativa  +  elemento }  println("sumaOfInts:  
$sumOfInts")
Agregue  
estas  
val  productOfInts  =  ints.fold(1)  { productoenejecución,  elemento  ­>  productoenejecución  *  elemento }  println("productoOfInts:  $productoOfInts")
líneas  a  la  función  principal.

val  nombres  =  comestibles.fold("")  { cadena,  elemento  ­>  cadena  +  println("nombres:  $nombres") "  ${elemento.nombre}" }

val  changeFrom50  =  comestibles.fold(50.0)  { cambio,  artículo
­>  cambiar  ­  item.unitPrice  *  item.quantity }
println("cambioDe50:  $cambioDe50")
}

Tomemos  el  código  para  una  prueba  de  manejo.

Prueba  de  conducción

Cuando  ejecutamos  el  código,  el  siguiente  texto  se  imprime  en  la  ventana  de  salida  del  IDE:

Verdura
Tomates
Hongos

Panadería
Bagels
Despensa
Aceite  de  oliva

Congelado

Helado
suma  de  enteros:  6

productOfInts:  6  
nombres:  Tomates  Champiñones  Bagels  Aceite  de  oliva  Helado  changeFrom50:  
22.0

388  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

P:  Usted  dijo  que  algunos  de  los  más  altos P:  ¿Tengo  que  proporcionar  siempre  la P:  ¿ Puedo  actualizar  las  variables  en  un


Las  funciones  de  orden  de  este  capítulo  no  se  pueden   función  de  pliegue  con  un  valor  inicial? cierre  de  lambda?
utilizar  directamente  con  un  mapa.  ¿Porqué  es  eso? ¿No  puedo  usar  el  primer  elemento  
de  la  colección  como  valor  inicial?
R:  Sí.
R:  Es  porque  Map  se  define  un  poco
de  manera  diferente  a  List  y  Set,  y  esto   R:  Cuando  utiliza  la  función  de  plegado , Como  recordará,  el  cierre  de  una  lambda  
afecta  qué  funciones  trabajarán  con  él. se  refiere  a  aquellas  variables  definidas  fuera
debe  especificar  el  valor  inicial.  Este  
parámetro  es  obligatorio  y  no  se   el  cuerpo  lambda  al  que  tiene  acceso  la  

Detrás  de  escena,  List  y  Set  heredan   puede  omitir. lambda.  A  diferencia  de  algunos  lenguajes  


el  comportamiento  de  una  interfaz  llamada como  Java,  puede  actualizar  estas  variables  
Sin  embargo,  si  desea  utilizar  el  primer   en  el  cuerpo  de  la  lambda  siempre  que  se  
Colección,  que  a  su  vez  hereda
comportamiento  definido  en  el  Iterable elemento  de  la  colección  como  valor   hayan  definido  mediante  var.
inicial,  un  enfoque  alternativo  es  utilizar  
interfaz.  Map,  sin  embargo,  no  hereda  de  
la  función  de  reducción .  Esta  función  funciona P:  ¿ Tiene  Kotlin  muchos  más
ninguna  de  estas  interfaces.  Esto  significa
de  forma  similar  a  fold,  excepto  que   funciones  de  orden  superior?
que  List  y  Set  son  ambos  tipos  de  
no  tienes  que  especificar  el  valor  inicial.
Iterable,  mientras  que  Map  no  lo  es.
Utiliza  automáticamente  el  primer  elemento  
R:  Sí.  Kotlin  tiene  demasiadas  funciones  superiores
de  la  colección  como  valor  inicial. funciones  de  orden  para  que  las  cubramos  en  una
Esta  distinción  es  importante  porque  
funciones  como  fold,  forEach  y   capítulo,  por  lo  que  decidimos  centrarnos  
P:  ¿Doble  iteración  a  través  de  la en  algunos  de  ellos:  los  que  creemos  que  son
groupBy  están  diseñadas  para  trabajar  
colección  en  un  orden  específico?   los  más  útiles  o  importantes.  Sin  embargo,  
con  Iterables.  Y  debido  a  que  Map  no  
¿Puedo  invertir  este  orden? ahora  que  sabe  cómo  usar  estas  funciones,  
es  Iterable,  obtendrá  un  error  de  compilación  
estamos  seguros  de  que  podrá  tomar  su  
si  intenta  usar  directamente  cualquiera  de  
conocimiento  y  aplicarlo  en  otros  lugares.
estas  funciones  con  Map. R:  Las  funciones  de  plegado  y  reducción.
trabaje  a  través  de  los  elementos  de  una  colección  de  
Puede  encontrar  una  lista  completa  de  las  funciones  de  Kotlin
izquierda  a  derecha,  comenzando  con  el  primer  elemento  
Sin  embargo,  la  buena  noticia  es  que  las   (incluidas  sus  funciones  de  orden  superior)  en  
de  la  colección.
propiedades  de  entradas,  claves  y  valores   la  documentación  en  línea:
de  Map  son  todos  tipos  de  iterables:  las  
Si  desea  invertir  este  orden,  puede  
entradas  y  las  claves  son  ambas. https://kotlinlang.org/api/latest/jvm/stdlib/
utilizar  las  funciones  foldRight  y   index.html
Conjuntos  y  valores  heredados  del
reduceRight .  Estas  funciones  
Interfaz  de  colección .  Esto  significa
funcionan  en  matrices  y  listas,  pero  no  en  
que  si  bien  no  puede  llamar  a  funciones  
conjuntos  o  mapas.
como  groupBy  y  plegar  en  un  mapa  
directamente,  aún  puede  usarlas  con  las  
propiedades  del  mapa.

estas  aqui  4 389
Machine Translated by Google

afila  tu  lápiz

El  siguiente  código  define  la  clase  de  datos  Grocery  y  un
List<Grocery>  comestibles  con  nombre :

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
val  unidad:  Cadena,  val  unidadPrecio:  Doble,
cantidad  de  valores:  Int)

val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0,  3),
Comestibles  ("Champiñones",  "Verduras",  "lb",  4.0,  1),
Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5,  2),
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))

Escribe  el  siguiente  código  para  saber  cuánto  se  gastará  en  vegetales.

Cree  una  Lista  que  contenga  el  nombre  de  cada  artículo  cuyo  precio  total  sea  inferior  a  5,0

Imprime  el  costo  total  de  cada  categoría.

Escribe  el  nombre  de  cada  artículo  que  no  viene  en  botella,  agrupados  por  unidad.

Respuestas  en  la  página  392.

390  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado
bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
diversión  principal(argumentos:  Array<String>)  {

val  myMap  =  mapOf("A"  a  4,  "B"  a  3,  "C"  a  2,  "D"  a  1,  "E"  a  2)
varx1  = ""
El  código  de  
var  x2  =  0
candidato  va  aquí.

Relaciona  
cada  candidato  
con  uno  de  los   imprimir("$x1$x2")
}
posibles  resultados.

Candidatos: Salida  posible:

x1  =  myMap.keys.fold("")  { x,  y  ­>  x  +  y}  x2  =  
10
myMap.entries.fold(0)  { x,  y  ­>  x  *  y.value }

ABCDE0
x2  =  myMap.values.groupBy  { it }.keys.sumBy  { it }

ABCDE48
x1  =  "ABCDE"

x2  =  miMapa.valores.fold(12)  { x,  y  ­>  x  ­  y }
43210

x2  =  myMap.entries.fold(1)  { x,  y  ­>  x  *  y.value } 432120

x1  =  miMapa.valores.fold("")  { x,  y  ­>  x  +  y } 48

x1  =  miMapa.valores.fold(0)  { x,  y  ­>  x  +  y } 125

.toString()  x2  
=  myMap.keys.groupBy  { it }.size Respuestas  en  la  página  393.

391
estas  aqui  4
Machine Translated by Google

afilar  solución

El  siguiente  código  define  la  clase  de  datos  Grocery  y  un
List<Grocery>  comestibles  con  nombre :

clase  de  datos  Supermercado  (nombre  de  valor:  cadena,  categoría  de  valor:  cadena,
val  unidad:  Cadena,  val  unidadPrecio:  Doble,
cantidad  de  valores:  Int)

val  comestibles  =  listaDe(Comestibles("Tomates",  "Verduras",  "lb",  3.0,  3),
Comestibles  ("Champiñones",  "Verduras",  "lb",  4.0,  1),
Comestibles  ("Bagels",  "Panadería",  "Paquete",  1.5,  2),
Supermercado("Aceite  de  oliva",  "Despensa",  "Botella",  6.0,  1),
Comestibles  ("Helado",  "Frozen",  "Pack",  3.0,  2))

Filtre  por  categoría,  luego  sume  
Escribe  el  siguiente  código  para  saber  cuánto  se  gastará  en  vegetales. el  precio  total.

groceries.filter  { it.category  ==  "Vegetable" }.sumByDouble  { it.unitPrice  *  it.quantity }

Cree  una  Lista  que  contenga  el  nombre  de  cada  artículo  cuyo  precio  total  sea  inferior  a  5,0
Filtre  por  

groceries.filter  { it.unitPrice  *  it.quantity  <  5.0 }.map  { it.name } precio  unitario  *  cantidad,  
luego  use  el  mapa  para  
transformar  el  resultado.
Imprime  el  costo  total  de  cada  categoría.
Para  cada  categoría...

comestibles.groupBy  { it.category }.forEach  

{ println("${it.key}:  ${it.value.sumByDouble  { it.unitPrice  *  it.quantity }}")
} ...  imprime  la  clave,  seguida  del  resultado  
de  sumByDouble  para  cada  valor.

Escribe  el  nombre  de  cada  artículo  que  no  viene  en  botella,  agrupados  por  unidad. Agrupa  los  resultados  por  unidad.

comestibles.filterNot  { it.unit  ==  "Bottle"}.groupBy  { it.unit }.forEach  {
Obtenga  las  entradas  donde  el   Imprime  cada  clave  en  el  Mapa  resultante.
println(it.key)  
valor  de  la  unidad  no  es  "Botella"

it.value.forEach  { println("  ${it.name}") }

} Cada  valor  en  el  Mapa  es  una  Lista<Comestibles>,  
para  que  podamos  usar
para  que  Cada  uno  recorra  cada  
Lista  e  imprima  el  nombre  de  cada  artículo.

392  Capítulo  12
Machine Translated by Google

funciones  integradas  de  orden  superior

A  continuación  se  muestra  un  breve  programa  de  Kotlin.  Falta  un  
bloque  del  programa.  Su  desafío  es  hacer  coincidir  el  bloque  de  código  
candidato  (a  la  izquierda),  con  el  resultado  que  vería  si  se  insertara  el  
bloque.  No  se  usarán  todas  las  líneas  de  salida  y  algunas  líneas  de  
salida  se  pueden  usar  más  de  una  vez.  Dibuje  líneas  que  conecten  los  
Mezclado bloques  de  código  candidatos  con  su  salida  correspondiente.
Mensajes
Solución
diversión  principal(argumentos:  Array<String>)  {

val  myMap  =  mapOf("A"  a  4,  "B"  a  3,  "C"  a  2,  "D"  a  1,  "E"  a  2)
varx1  = ""

El  código  de   var  x2  =  0
candidato  va  aquí.

imprimir("$x1$x2")
}

Candidatos: Salida  posible:

x1  =  myMap.keys.fold("")  { x,  y  ­>  x  +  y}  x2  =  
10
myMap.entries.fold(0)  { x,  y  ­>  x  *  y.value }

ABCDE0
x2  =  myMap.values.groupBy  { it }.keys.sumBy  { it }

ABCDE48
x1  =  "ABCDE"

x2  =  miMapa.valores.fold(12)  { x,  y  ­>  x  ­  y }
43210

x2  =  myMap.entries.fold(1)  { x,  y  ­>  x  *  y.value } 432120

x1  =  miMapa.valores.fold("")  { x,  y  ­>  x  +  y } 48

x1  =  miMapa.valores.fold(0)  { x,  y  ­>  x  +  y } 125

.toString()  x2  
=  myMap.keys.groupBy  { it }.size

estas  aqui  4 393
Machine Translated by Google

caja  de  herramientas

Tu  caja  de  herramientas  de  Kotlin
Puede  descargar  
Tiene  el  Capítulo  12  en  su  haber  y  ahora  ha   el  código  
agregado  funciones  integradas  de  orden  superior   completo  del  
a  su  caja  de  herramientas. capítulo  desde  https://
tinyurl.com/HFKotlin.
CAPÍTULO  
12

Use  minBy  y  maxBy  para  encontrar  el  valor  más  bajo   La  función  de  mapa  transforma  los  elementos  de  
o  más  alto  en  una  colección.  Estas  funciones  toman  un   una  colección  de  acuerdo  con  algunos  criterios  que  
parámetro,  una  lambda  cuyo  cuerpo  especifica  los  criterios   especifica  mediante  una  lambda.  Devuelve  una  Lista.
de  la  función.  El  tipo  de  devolución  coincide  con  el  tipo  de  
forEach  funciona  como  un  bucle  for.  Le  permite  realizar  
elementos  de  la  colección.
una  o  más  acciones  para  cada  elemento  de  una  colección.
Utilice  sumBy  o  sumByDouble  para  devolver  la  suma  
de  los  elementos  de  una  colección.  Su  parámetro,  una  
Use  groupBy  para  dividir  una  colección  en  grupos.
lambda,  especifica  lo  que  desea  sumar.
Toma  un  parámetro,  una  lambda,  que  define  cómo  la  
Si  es  un  Int,  use  sumBy,  y  si  es  un  Double,  use  
función  debe  agrupar  los  elementos.  La  función  devuelve  
sumByDouble.
un  Mapa,  que  utiliza  los  criterios  lambda  para  las  claves,  
La  función  de  filtro  le  permite  buscar  o  filtrar  una  colección   y  una  Lista  para  cada  valor.
de  acuerdo  con  algunos  criterios.  Este  criterio  se  especifica  
La  función  de  plegado  le  permite  especificar  un  valor  
mediante  una  lambda,  cuyo  cuerpo  de  lambda  debe  
inicial  y  realizar  alguna  operación  para  cada  elemento  de  
devolver  un  valor  booleano.  El  filtro  generalmente  devuelve  
una  colección.  Toma  dos  parámetros:  el  valor  inicial  y  
una  Lista.  Sin  embargo,  si  la  función  se  utiliza  con  un  
una  lambda  que  especifica  la  operación  que  desea  realizar.
mapa,  devuelve  un  mapa  en  su  lugar.

394  Capítulo  12
Machine Translated by Google

Saliendo  de  la  ciudad...

Ha  sido  genial  tenerte  aquí  en  Kotlinville.

Nos  entristece  que  te  vayas,  pero  no  hay  nada  como  tomar  lo  que  has  aprendido.
y  ponerlo  en  uso.  Todavía  hay  algunas  gemas  más  para  usted  en  la  parte  posterior  del  libro  y  un
práctico  índice,  y  luego  es  hora  de  tomar  todas  estas  nuevas  ideas  y  ponerlas  en  práctica.  bon
¡viaje!
Machine Translated by Google
Machine Translated by Google

apéndice  i:  rutinas

Correr Código  en Paralelo


¿ Quieres  decir  que  

puedo  caminar  y  mascar  
chicle  al  mismo  tiempo?  

¡Que  interesante!

Algunas  tareas  se  realizan  mejor  en  segundo  plano.
Si  desea  leer  datos  de  un  servidor  externo  lento,  probablemente  no  desee  que  el  resto  de  su  código  permanezca  

esperando  a  que  se  complete  el  trabajo.  En  situaciones  como  estas,  las  corrutinas  son  tu  nueva  mejor  amiga.  Las  

rutinas  le  permiten  escribir  código  que  se  ejecuta  de  forma  asincrónica.

Esto  significa  menos  tiempo  dando  vueltas,  una  mejor  experiencia  de  usuario  y  también  puede  hacer  que  su  

aplicación  sea  más  escalable.  Sigue  leyendo  y  aprenderás  el  secreto  de  cómo  hablar  con  Bob  mientras  escuchas  a  

Suzy  al  mismo  tiempo.

esto  es  un  apéndice  397
Machine Translated by Google

crear  proyecto

Construyamos  una  caja  de  ritmos
El  código  de  este  
Las  corrutinas  le  permiten  crear  múltiples  piezas  de  código  que  pueden  
ejecutarse  de  forma  asincrónica.  En  lugar  de  ejecutar  piezas  de  código  en  secuencia,  
una  tras  otra,  las  corrutinas  le  permiten  ejecutarlas  una  al  lado  de  la  otra.
apéndice  se  aplica  a  
Kotlin  1.3  y  versiones  
El  uso  de  rutinas  significa  que  puede  iniciar  un  trabajo  en  segundo  plano,  como  
leer  datos  de  un  servidor  externo,  sin  que  el  resto  de  su  código  tenga  que  esperar  
posteriores.  En  
a  que  se  complete  el  trabajo  antes  de  hacer  cualquier  otra  cosa.
Esto  le  brinda  a  su  usuario  una  experiencia  más  fluida  y  también  hace  que  su  
versiones  anteriores,  las  
aplicación  sea  más  escalable.

Para  ver  la  diferencia  que  el  uso  de  rutinas  puede  hacer  en  su  código,  suponga  
corrutinas  se  marcaban  
que  desea  construir  una  caja  de  ritmos  basada  en  algún  código  que  reproduzca  
una  secuencia  de  ritmo  de  batería.  Comencemos  por  crear  el  proyecto  de  Drum   como  experimentales.
Machine  siguiendo  los  siguientes  pasos.

1.  Crear  un  nuevo  proyecto  GRADLE Gradle  es  una  herramienta  de  compilación  
que  le  permite  compilar  e  implementar  código  
Para  escribir  código  que  use  corrutinas,  necesitamos  crear  un  nuevo  proyecto   e  incluir  cualquier  biblioteca  de  terceros  que  
Gradle  para  que  podamos  configurarlo  para  usar  corrutinas.  Para  hacer  esto,  cree  un   necesite  su  código.  Estamos  usando  Gradle  
nuevo  proyecto,  seleccione  la  opción  Gradle  y  marque  Kotlin  (Java).  Luego  haga  clic   aquí  para  poder  agregar  rutinas  a  nuestro  
en  el  botón  Siguiente.
proyecto  unas  páginas  más  adelante.

Elija  la  opción  Kotlin  (Java),  ya  que  estamos  
usando  Kotlin  para  apuntar  a  la  JVM.

Seleccione  
la  opción  Gradle.

398  apéndice  i
Machine Translated by Google

corrutinas

2.  Ingrese  una  ID  de  artefacto

Cuando  crea  un  proyecto  de  Gradle,  debe  especificar  un  ID  de  
artefacto.  Este  es  básicamente  el  nombre  del  proyecto,  excepto  que,  
por  convención,  debe  estar  en  minúsculas.  Ingrese  una  ID  de  artefacto  
de  "drummachine",  luego  haga  clic  en  el  botón  Siguiente.

Estamos  utilizando  un  ID  de  artefacto  de  "drummachine".

3.  Especifique  los  detalles  de  configuración

A  continuación,  debe  especificar  cualquier  cambio  en  la  configuración  
predeterminada  del  proyecto.  Haga  clic  en  el  botón  Siguiente  para  aceptar  los  valores  predeterminados.

Acepte  los  valores  predeterminados  
haciendo  clic  en  el  botón  Siguiente.

estas  aqui  4 399
Machine Translated by Google

agregar  archivos

4.  Especifique  el  nombre  del  proyecto

Finalmente,  necesitamos  especificar  un  nombre  de  proyecto.  Nombre  el  
proyecto  "Drum  Machine",  luego  haga  clic  en  el  botón  Finalizar.  IntelliJ  IDEA  
creará  su  proyecto.

Hemos  llamado  a  nuestro  proyecto  “Drum  Machine”.

Agregar  los  archivos  de  audio

Ahora  que  ha  creado  el  proyecto  Drum  Machine,  necesita  agregarle  un  par  de  
archivos  de  audio.  Descargue  los  archivos  crash_cymbal.aiff  y  toms.aiff  de  
https://tinyurl.com/HFKotlin,  luego  arrástrelos  a  su  proyecto.  Cuando  se  le  
solicite,  confirme  que  desea  moverlos  a  la  carpeta  Drum  Machine .

Estamos  agregando  los  archivos  
al  directorio  raíz  de  nuestro  proyecto.

400  apéndice  i
Machine Translated by Google

corrutinas

Agregar  el  código  al  proyecto
Nos  han  dado  un  código  que  reproduce  una  secuencia  de  batería,  que  debemos  
agregar  al  proyecto.  Cree  un  nuevo  archivo  de  Kotlin  llamado  Beats.kt  
resaltando  la  carpeta  src/main/kotlin ,  haciendo  clic  en  el  menú  Archivo  y  
eligiendo  Nuevo  →  Archivo/clase  de  Kotlin.  Cuando  se  le  solicite,  nombre  el  
archivo  "Beats"  y  seleccione  Archivo  en  la  opción  Tipo.  Luego  actualice  su  versión  
de  Beats.kt  para  que  coincida  con  la  nuestra  a  continuación:
Estamos  usando  dos  bibliotecas  de  Java,  por  lo  que  
importar  java.io.File  importar   debemos  importarlas.  Puede  obtener  más  información  
javax.sound.sampled.AudioSystem sobre  las  declaraciones  de  importación  en  el  Apéndice  III.

diversión  playBeats(ritmos:  Cadena,  archivo:  Cadena)  { val  partes  =   El  parámetro  de  tiempos  especifica  el  patrón  de  
beats.split("x") tiempos.  El  parámetro  de  archivo  especifica  el  archivo  
recuento  de  variables  =  0 de  sonido  a  reproducir.
para  (parte  en  partes)  {
cuenta  +=  parte.longitud  +  1  if  (parte  ==  
"")  { reproducirSonido(archivo) }  else  {

Detiene  el  hilo  de  ejecución  
actual  para  que  el  archivo  de  
Thread.sleep(100  *  (parte.longitud  +  1L))  if  (recuento  
Llame  a  playSound  una   sonido  tenga  tiempo  de  ejecutarse.
<latidos.longitud)  {
vez  por  cada  "x"  en  el  
reproducir  sonido  (archivo)
parámetro  de  tiempos.
}
caja  de  ritmos
}
}
} src/principal/kotlin
Reproduce  el  archivo  de  audio  especificado.

divertido  playSound  (archivo:  Cadena)  {
Beats.kt
val  clip  =  AudioSystem.getClip()  val  audioInputStream  
=  AudioSystem.getAudioInputStream(
Archivo(
archivo

)
)

clip.open(audioInputStream)  clip.start()

diversión  principal  ()  {
Reproduce  los  archivos  de  sonido  de  timbales  y  platillos.
playBeats("xxxxxx­",  "toms.aiff")  playBeats("x­­­­­x­­­­­",  
"crash_cymbal.aiff")
}

Veamos  qué  sucede  cuando  se  ejecuta  el  código.

estas  aqui  4 401
Machine Translated by Google

prueba  de  manejo

Prueba  de  conducción

Cuando  ejecutamos  el  código,  toca  primero  los  timbales  (toms.aiff),  seguidos  de  
los  platillos  (crash_cymbal.aiff).  Hace  esto  en  secuencia,  así  que  una  vez  que  los  
timbales  han  terminado,  los  platillos  comienzan  a  sonar:

¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  Tish!  Tish!
El  código  reproduce  el  archivo  de   Luego  reproduce  el  

sonido  de  toms  seis  veces. archivo  de  sonido  de  
platillos  dos  veces.

Pero,  ¿y  si  queremos  tocar  los  timbales  y  los  platillos  en  paralelo?

Usa  rutinas  para  hacer  que  los  ritmos  suenen  en  paralelo

Como  dijimos  anteriormente,  las  corrutinas  le  permiten  ejecutar  múltiples  piezas  
de  código  de  forma  asíncrona.  En  nuestro  ejemplo,  esto  significa  que  podemos  
agregar  nuestro  código  de  tambor  tom  a  una  rutina  para  que  suene  al  mismo  
tiempo  que  los  platillos.

Hay  dos  cosas  que  tenemos  que  hacer  para  lograr  esto:

1 Agregue  coroutines  al  proyecto  como  una  dependencia.
Las  rutinas  están  en  una  biblioteca  Kotlin  separada,  que  debemos  
agregar  a  nuestro  proyecto  antes  de  poder  usarlas.

2 Inicie  una  rutina.
La  rutina  incluirá  el  código  que  reproduce  los  timbales.

Hagamos  esto  ahora.

402  apéndice  i
Machine Translated by Google

corrutinas

1.  Agrega  una  dependencia  de  rutinas
Si  desea  utilizar  rutinas  en  su  proyecto,  primero  debe  agregarlo  a  su  proyecto  como  
una  dependencia.  Para  hacer  esto,  abra  build.gradle  y  actualice  la  sección  de  
dependencias  así:

dependencias  {
caja  de  ritmos
compilar  "org.jetbrains.kotlin:kotlin­stdlib­jdk8"
implementación  'org.jetbrains.kotlinx:kotlinx­coroutines­core:1.0.1'
construir.gradle
}
Agregue  esta  línea  a  build.gradle  
para  agregar  la  biblioteca  de  rutinas  
Luego  haga  clic  en  el  indicador  Importar  cambios  para  que  el  cambio  surta  
efecto: a  su  proyecto.

Haga  clic  en  Importar  cambios  
si  se  le  solicita  que  lo  haga.

A  continuación,  actualizaremos  nuestra  función  principal  para  que  use  una  rutina.

2.  Inicie  una  rutina
Haremos  que  nuestro  código  reproduzca  el  archivo  de  sonido  de  los  toms  
en  una  rutina  separada  en  segundo  plano  encerrando  el  código  que  lo  
reproduce  en  una  llamada  a  GlobalScope.launch  desde  kotlinx.  biblioteca  de  
corrutinas.  Detrás  de  escena,  esto  hace  que  el  código  que  reproduce  el  archivo  de  
sonido  de  toms  se  ejecute  en  segundo  plano  para  que  los  dos  sonidos  se  
reproduzcan  en  paralelo.

Aquí  está  la  nueva  versión  de  nuestra  función  principal:  actualice  su  código  con  
nuestros  cambios  (en  negrita):

...

importar  kotlinx.coroutines.* Agregue  esta  línea  para  que  
caja  de  ritmos
... puedo  usar funcionemos  desde  la  

biblioteca  coroutines  en  nuestro  código.
src/principal/kotlin
diversión  principal  ()  {
Inicie  una   GlobalScope.lanzamiento  { playBeats("xxxxxx­",  "toms.aiff") }
corrutina  en   Beats.kt
playBeats("x­­­­­x­­­­­",  "crash_cymbal.aiff")
segundo  plano.
}

Veamos  esto  en  acción  tomando  el  código  para  una  prueba  de  manejo.

estas  aqui  4 403
Machine Translated by Google

corrutinas  vs  hilos

Prueba  de  conducción

Cuando  ejecutamos  el  código,  toca  los  timbales  y  platillos  en  paralelo.  El  sonido  de  los  
timbales  se  reproduce  en  una  rutina  separada  de  fondo.

¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!
Tish! Tish!

Esta  vez,  los  timbales  y  los  
platillos  tocan  en  paralelo.

Ahora  que  ha  visto  cómo  iniciar  una  corrutina  en  segundo  plano  y  el  efecto  que  esto  tiene,  
profundicemos  un  poco  más  en  las  corrutinas.

Una  rutina  es  como  un  hilo  ligero.
Detrás  de  escena,  lanzar  una  corrutina  es  como  comenzar  un  hilo  de  ejecución  por  
separado .  Los  subprocesos  son  muy  comunes  en  otros  lenguajes  como  Java,  y  tanto  las  
corrutinas  como  los  subprocesos  pueden  ejecutarse  en  paralelo  y  comunicarse  entre  sí.  La  
diferencia  clave,  sin  embargo,  es  que  es  más  eficiente  usar  rutinas  en  su  código  que  usar  
subprocesos.

Comenzar  un  hilo  y  mantenerlo  funcionando  es  bastante  costoso  en  términos  de  
rendimiento.  Por  lo  general,  el  procesador  solo  puede  ejecutar  una  cantidad  limitada  de  
subprocesos  al  mismo  tiempo,  y  es  más  eficiente  ejecutar  la  menor  cantidad  de  
subprocesos  posible.  Las  corrutinas,  por  otro  lado,  se  ejecutan  en  un  grupo  compartido  de  
subprocesos  de  forma  predeterminada,  y  el  mismo  subproceso  puede  ejecutar  muchas  
corrutinas.  Como  se  usan  menos  subprocesos,  esto  hace  que  sea  más  eficiente  usar  
rutinas  cuando  desea  ejecutar  tareas  de  forma  asíncrona.

En  nuestro  código,  usamos  GlobalScope.launch  para  ejecutar  una  nueva  rutina  
en  segundo  plano.  Detrás  de  escena,  esto  crea  un  nuevo  subproceso  en  el  que  se  
ejecuta  la  rutina,  de  modo  que  toms.aiff  y  crash_cymbal.aiff  se  reproducen  en  subprocesos  
separados.  Como  es  más  eficiente  usar  la  menor  cantidad  de  subprocesos  posible,  
busquemos  cómo  podemos  reproducir  los  archivos  de  sonido  en  rutinas  separadas,  pero  
en  el  mismo  subproceso.

404  apéndice  i
Machine Translated by Google

corrutinas

Use  runBlocking  para  ejecutar  rutinas  en  el  mismo  ámbito
Si  desea  que  su  código  se  ejecute  en  el  mismo  subproceso  pero  en  rutinas  separadas,  puede  usar  la  
función  runBlocking .  Esta  es  una  función  de  orden  superior  que  bloquea  el  subproceso  actual  hasta  
que  el  código  que  se  le  pasa  termina  de  ejecutarse.  La  función  runBlocking  define  un  alcance  que  es  
heredado  por  el  código  que  se  le  pasa;  en  nuestro  ejemplo,  podemos  usar  este  alcance  para  ejecutar  
rutinas  separadas  en  el  mismo  hilo.

Aquí  hay  una  nueva  versión  de  nuestra  función  principal  que  hace  esto:  actualice  su  versión  del  
código  para  incluir  nuestros  cambios  (en  negrita):

diversión  principal  ()  { Envuelva  el  código  que  queremos  
ejecutar  en  una  llamada  a  runBlocking. caja  de  ritmos
ejecutarBlocking  {
Quite  la  
GlobalScope.lanzamiento  {playBeats("xxxxxx­",  "toms.aiff") }
referencia   src/principal/kotlin

a  GlobalScope. playBeats("x­­­­­x­­­­­",  "crash_cymbal.aiff")
}
Beats.kt
}

Tenga  en  cuenta  que  ahora  estamos  iniciando  una  nueva  corrutina  utilizando  el  lanzamiento  en  lugar  
de  GlobalScope.launch.  Esto  se  debe  a  que  queremos  lanzar  una  corrutina  que  se  ejecute  en  el  
mismo  subproceso,  en  lugar  de  en  un  subproceso  de  fondo  separado,  y  omitir  la  referencia  a  
GlobalScope  permite  que  la  corrutina  use  el  mismo  alcance  que  runBlocking.

Veamos  qué  sucede  cuando  ejecutamos  el  código.

Prueba  de  conducción

Cuando  ejecutamos  el  código,  los  archivos  de  sonido  se  reproducen,  pero  en  secuencia,  no  en  paralelo.

Tish!  Tish!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!
El  código  reproduce  el  
Luego  reproduce  el  archivo  de  sonido  de  
archivo  de  sonido  de  platillos  dos  veces. toms  seis  veces.

Entonces,  ¿qué  salió  mal?

estas  aqui  4 405
Machine Translated by Google

Thread.sleep  vs  retraso

Thread.sleep  pausa  el  THREAD  actual
Como  habrás  notado,  cuando  agregamos  la  función  playBeats  a  nuestro  proyecto,  
incluimos  la  siguiente  línea:

Thread.sleep(100  *  (part.length  +  1L))

Esto  utiliza  una  biblioteca  de  Java  para  pausar  el  hilo  actual  para  que  el  archivo  de  sonido  
que  se  está  reproduciendo  tenga  tiempo  de  ejecutarse  y  bloquea  el  hilo  para  que  no  haga  
nada  más.  Como  ahora  estamos  reproduciendo  los  archivos  de  sonido  en  el  mismo  hilo,  
ya  no  se  pueden  reproducir  en  paralelo,  aunque  estén  en  rutinas  separadas.

La  función  de  retardo  pausa  la  CORUTINA  actual
Un  mejor  enfoque  en  esta  situación  es  utilizar  la  función  de  retardo  de  rutinas  en  su  lugar.  
Esto  tiene  un  efecto  similar  a  Thread.sleep,  excepto  que  en  lugar  de  pausar  el  hilo  actual,  
pausa  la  rutina  actual.  Suspende  la  rutina  durante  un  período  de  tiempo  específico  y  esto  
permite  que  se  ejecute  otro  código  en  el  mismo  subproceso.  El  siguiente  código,  por  
ejemplo,  retrasa  la  rutina  durante  1  segundo:

La  función  de  retraso  agrega  una  pausa,  pero  
retraso  (1000)
es  más  eficiente  que  usar  Thread.sleep.

La  función  de  retardo  se  puede  utilizar  en  estas  dos  situaciones:

¥ Desde  dentro  de  una  rutina.
El  siguiente  código,  por  ejemplo,  llama  a  la  función  de  retraso  dentro  de  una  
rutina:

GlobalScope.lanzamiento  {
Aquí,  estamos  lanzando  la  rutina  y  
retraso  (1000) luego  retrasando  su  código  por  1  segundo.
//  código  que  se  ejecuta  después  de  1  segundo

¥ Desde  dentro,  una  función  que  el  compilador  sabe  puede  pausar  o  
suspender.
Cuando  llama  a  
En  nuestro  ejemplo,  queremos  usar  la  función  de  retraso  dentro  de  la  función  playBeats,  lo  
que  significa  que  debemos  decirle  al  compilador  que  playBeats,  y  la  función  principal  que  lo   una  función  
llama,  puede  suspenderse.  Para  hacer  esto,  agregaremos  el  prefijo  de  suspensión  a  ambas  
funciones  usando  un  código  como  este: suspendible  (como  un  
suspender  playBeats  divertidos  (ritmos:  Cadena,  archivo:  Cadena)  { retraso)  desde  otra  
...
El  prefijo  de  suspensión  le  dice  al  compilador  
}
que  la  función  puede  suspenderse.
función,  esa  función  
Le  mostraremos  el  código  completo  del  proyecto  en  la  página  siguiente. debe  marcarse  con  suspensión
406  apéndice  i
Machine Translated by Google

corrutinas

El  código  completo  del  proyecto.

Aquí  está  el  código  completo  para  el  proyecto  Drum  Machine:  actualice  su  
versión  de  Beats.kt  para  incluir  nuestros  cambios  (en  negrita):

importar  java.io.Archivo
importar  javax.sound.sampled.AudioSystem
importar  kotlinx.coroutines.*

suspender  playBeats  divertidos  (ritmos:  Cadena,  archivo:  Cadena)  {
val  partes  =  beats.split("x")
Marque  playBeats   recuento  de  variables  =  0
con  suspender  
para  (parte  en  partes)  {
para  que  pueda  
cuenta  +=  longitud  parcial  +  1
llamar  a  la  función  de  retraso.
si  (parte  ==  "")  {
reproducir  sonido  (archivo)
}  demás  {
Reemplace  Thread.sleep   Thread.retraso  del  sueño  (100  *  (part.length  +  1L))
con  retraso. if  (cuenta  <  latidos.longitud)  {
reproducir  sonido  (archivo)
}
caja  de  ritmos
}
}
src/principal/kotlin
}

divertido  playSound  (archivo:  Cadena)  { Beats.kt

val  clip  =  AudioSystem.getClip()
val  audioInputStream  =  AudioSystem.getAudioInputStream(
Archivo(
archivo

clip.open(flujo  de  entrada  de  audio)
clip.inicio()
}

Marque  
suspender  fun  main()  {
principal  con  
ejecutarBlocking  {
suspender  para  
iniciar  {playBeats("xxxxxx­",  "toms.aiff") }
que  pueda  llamar  
a  la  función  playBeats. playBeats("x­­­­­x­­­­­",  "crash_cymbal.aiff")
}
}

Veamos  qué  sucede  cuando  se  ejecuta  el  código.

estas  aqui  4 407
Machine Translated by Google

prueba  de  manejo

Prueba  de  conducción

Cuando  ejecutamos  el  código,  toca  los  timbales  y  platillos  en  paralelo  como  antes.  Esta  vez,  sin  
embargo,  los  archivos  de  sonido  se  ejecutan  en  rutinas  separadas  en  el  mismo  hilo.

¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!  ¡Bam!
Tish! Tish!
Los  timbales  y  los  platillos  aún  se  reproducen  en  paralelo,  pero  esta  vez  
estamos  usando  una  forma  más  eficiente  de  reproducir  los  archivos  de  sonido.

Puede  obtener  más  información  sobre  el  uso  de  rutinas  aquí:

https://kotlinlang.org/docs/reference/coroutines­overview.html

Las  corrutinas  le  permiten  ejecutar   La  función  runBlocking  bloquea  el  

código  de  forma  asincrónica.  Son  útiles  para   subproceso  actual  hasta  que  el  código  que  

ejecutar  tareas  en  segundo  plano. contiene  haya  terminado  de  ejecutarse.

Una  rutina  es  como  un  hilo  ligero.  Las   La  función  de  retraso  suspende  el  código  
corrutinas  se  ejecutan  en  un  grupo  compartido   durante  un  período  de  tiempo  específico.
de  subprocesos  de  forma  predeterminada,  y  el   Se  puede  usar  dentro  de  una  corrutina  o  dentro  

mismo  subproceso  puede  ejecutar  muchas   de  una  función  marcada  con  suspensión.
corrutinas.

Para  usar  rutinas,  cree  un  proyecto  de  Gradle  
Puede  descargar  
y  agregue  la  biblioteca  de  rutinas  a  build.gradle  
el  código  completo  
como  una  dependencia.
de  este  apéndice  
Use  la  función  de  lanzamiento  para  lanzar  una  
nueva  rutina.
desde  https://
tinyurl.com/HFKotlin.

408  apéndice  i
Machine Translated by Google

apéndice  ii:  pruebas

Sostener Su Código  

No  me  importa  
para Cuenta
quién  eres,  o  qué  tan  duro  
te  ves.  Si  no  sabes  la  
contraseña,  no  vas  a  
entrar.

Todo  el  mundo  sabe  que  un  buen  código  debe  funcionar.
Pero  cada  cambio  de  código  que  realice  corre  el  riesgo  de  introducir  nuevos  errores  que  impidan  que  su  

código  funcione  como  debería.  Es  por  eso  que  las  pruebas  exhaustivas  son  tan  importantes:  significa  que  

puede  conocer  cualquier  problema  en  su  código  antes  de  que  se  implemente  en  el  entorno  en  vivo.

En  este  apéndice,  analizaremos  JUnit  y  KotlinTest,  dos  bibliotecas  que  puede  usar  para  realizar  pruebas  

unitarias  de  su  código  para  que  siempre  tenga  una  red  de  seguridad.

esto  es  un  apéndice  409
Machine Translated by Google

JUnit

Kotlin  puede  usar  bibliotecas  de  prueba  existentes
Como  ya  sabe,  el  código  de  Kotlin  se  puede  compilar  en  Java,  JavaScript  o  código  
nativo,  por  lo  que  puede  usar  las  bibliotecas  existentes  en  su  plataforma  de  destino.  
Cuando  se  trata  de  pruebas,  esto  significa  que  puede  probar  el  código  de  Kotlin  
utilizando  las  bibliotecas  de  prueba  más  populares  en  Java  y  JavaScript.

Veamos  cómo  usar  JUnit  para  probar  unitariamente  su  código  Kotlin.

Agregar  la  biblioteca  JUnit

La  biblioteca  JUnit  (https://junit.org)  es  la  biblioteca  de  pruebas  de  Java  más  utilizada.
Las  pruebas  unitarias  se  
utilizan  para  probar  unidades  
Para  usar  JUnit  en  su  proyecto  Kotlin,  primero  debe  agregar  las  bibliotecas  JUnit  a  su  
proyecto.  Puede  agregar  bibliotecas  a  su  proyecto  yendo  al  menú  Archivo  y  eligiendo   individuales  de  código  fuente,  
Estructura  del  proyecto  →  Bibliotecas  o,  si  tiene  un  proyecto  Gradle,  puede  agregar  estas  
como  clases  o  funciones.
líneas  a  su  archivo  build.gradle :

dependencias  {
.... Estas  líneas  
implementación  de  prueba  'org.junit.jupiter:junit­jupiter­api:5.3.1' agregan  la  versión  
5.3.1  de  las  
testRuntimeOnly  'org.junit.jupiter:junit­jupiter­engine:5.3.1'
prueba  {usarJUnitPlatform()} bibliotecas  JUnit  al  
proyecto.  Cambie  los  
....
números  si  desea  
} utilizar  una  versión  diferente.

Una  vez  que  se  compila  el  código,  puede  ejecutar  las  pruebas  haciendo  clic  con  
el  botón  derecho  en  el  nombre  de  la  clase  o  función  y  luego  seleccionando  la  
opción  Ejecutar.

Para  ver  cómo  usar  JUnit  con  Kotlin,  vamos  a  escribir  una  prueba  para  la  siguiente  
clase  llamada  Totaller:  la  clase  se  inicializa  con  un  valor  Int  y  mantiene  un  total  
acumulado  de  los  valores  que  se  le  agregan  usando  su  add  función:

clase  Totalizador(var  total:  Int  =  0)  {
divertido  agregar  (num:  Int):  Int  {
total  +=  número
devolución  total

}
}

Veamos  cómo  sería  una  prueba  JUnit  para  esta  clase.

410  apéndice  ii
Machine Translated by Google

pruebas

Crear  una  clase  de  prueba  JUnit

Aquí  hay  un  ejemplo  de  clase  de  prueba  JUnit  llamada  TotallerTest  que  se  usa  
para  probar  Totaller:
Estamos  usando  código  de  los  paquetes  JUnit,  por  lo  que  
importar  org.junit.jupiter.api.Assertions.* debemos  importarlos.  Puede  obtener  más  información  

importar  org.junit.jupiter.api.Test sobre  las  declaraciones  de  importación  en  el  Apéndice  III.

La  clase  TotallerTest  se  utiliza  para  probar  Totaller.
clase  TotallerTest  {

@Prueba Esta  es  una  anotación  que  marca  la  siguiente  función  como  una  prueba.
diversión  debería  ser  capaz  de  agregar  3  y  4  ()  {
Cree  un  objeto  Totaller.
val  totalizador  =  totalizador()

Comprueba  que  si  sumamos  3,  el  valor  devuelto  es  3.
afirmarEquals(3,  totaler.add(3))

afirmarEquals(7,  totaler.add(4)) Si  ahora  sumamos  4,  el  valor  devuelto  debería  ser  7.

afirmarEquals(7,  totalizador.total) Compruebe  que  el  valor  devuelto  coincida  
} con  el  valor  de  la  variable  total.
}

Cada  prueba  se  lleva  a  cabo  en  una  función,  con  el  prefijo  @Test.
Las  anotaciones  se  utilizan  para  agregar  información  programática  sobre  su  código,  
y  la  anotación  @Test  es  una  forma  de  decirle  a  las  herramientas  "Esta  es  una  
función  de  prueba".

Las  pruebas  se  componen  de  acciones  y  afirmaciones.  Las  acciones  son  piezas  de  
código  que  hacen  cosas,  mientras  que  las  aserciones  son  piezas  de  código  que   Puede  obtener  más  
información  sobre  
verifican  cosas.  En  el  código  anterior,  estamos  usando  una  aserción  llamada  
assertEquals  que  verifica  que  los  dos  valores  que  se  le  dan  sean  iguales.  Si  no  lo   el  uso  de  JUnit  
son,  assertEquals  lanzará  una  excepción  y  la  prueba  fallará.
aquí:  https://junit.org
En  el  ejemplo  anterior,  nombramos  nuestra  función  de  prueba  
shouldBeAbleToAdd3And4.  Sin  embargo,  podemos  usar  una  función  de  Kotlin  
que  se  usa  con  poca  frecuencia  y  que  nos  permite  envolver  los  nombres  de  las  
funciones  en  comillas  invertidas  (`)  y  luego  agregar  espacios  y  otros  símbolos  al  
nombre  de  la  función  para  que  sea  más  descriptivo.  Aquí  hay  un  ejemplo:
Esto  parece  extraño,  pero  es  un  
....
nombre  de  función  de  Kotlin  válido.
@Prueba

fun  ̀debe  poder  sumar  3  y  4  ­  y  no  debe  salir  mal`()  {

val  totalizador  =  totalizador()
...

En  su  mayor  parte,  usa  JUnit  en  Kotlin  casi  de  la  misma  manera  que  podría  usarlo  
con  un  proyecto  Java.  Pero  si  quieres  algo  un  poco  más  Kotliny,  hay  otra  biblioteca  
que  puedes  usar,  llamada  KotlinTest.

estas  aqui  4 411
Machine Translated by Google

Prueba  de  Kotlin

Usando  KotlinTest
La  biblioteca  KotlinTest  (https://github.com/kotlintest/kotlintest)  ha  sido  diseñada  
para  usar  la  amplitud  completa  del  lenguaje  Kotlin  para  escribir  pruebas  de  
una  manera  más  expresiva.  Al  igual  que  JUnit,  es  una  biblioteca  separada  que  
debe  agregarse  a  su  proyecto  si  desea  usarla.

KotlinTest  es  bastante  amplio  y  le  permite  escribir  pruebas  en  muchos  estilos  
diferentes,  pero  aquí  hay  una  forma  de  escribir  una  versión  de  KotlinTest  del  
código  JUnit  que  escribimos  anteriormente:

Estamos  usando  estas  funciones  de  las  bibliotecas  de  
importar  io.kotlintest.shouldBe KotlinTest,  por  lo  que  debemos  importarlas.
importar  io.kotlintest.specs.StringSpec

La  función  de  prueba  JUnit  se  reemplaza  con  una  cadena.
clase  OtraPruebaTotal:  StringSpec({
"debería  poder  sumar  3  y  4,  y  no  debe  salir  mal" {
val  totalizador  =  totalizador()

totaler.add(3)  debería  ser  3
totaler.add(4)  debería  ser  7 Estamos  usando  shouldBe  en  lugar  de  assertEquals.
totaler.total  debe  ser  7

}
})

La  prueba  anterior  se  parece  a  la  prueba  JUnit  que  vio  anteriormente,  excepto  
que  la  función  de  prueba  se  reemplaza  con  una  cadena  y  las  llamadas  a  
assertEquals  se  han  reescrito  como  expresiones  shouldBe.
Este  es  un  ejemplo  del  estilo  String  Specification  (o  StringSpec)  de  
KotlinTest.  Hay  varios  estilos  de  prueba  disponibles  en  KotlinTest,  y  debe  
elegir  el  que  mejor  se  adapte  a  su  código.

Pero  KotlinTest  no  es  solo  una  reescritura  de  JUnit  (de  hecho,  KotlinTest  usa  
JUnit  bajo  el  capó).  KotlinTest  tiene  muchas  más  funciones  que  pueden  permitirle  
crear  pruebas  más  fácilmente  y  con  menos  código  de  lo  que  puede  hacer  con  
una  simple  biblioteca  de  Java.  Puede,  por  ejemplo,  usar  filas  para  probar  su  
código  con  conjuntos  completos  de  datos.  Veamos  un  ejemplo.

412  apéndice  ii
Machine Translated by Google

pruebas

Use  filas  para  probar  contra  conjuntos  de  datos
Aquí  hay  un  ejemplo  de  una  segunda  prueba  que  usa  filas  para  sumar  muchos  
números  diferentes  (nuestros  cambios  están  en  negrita):

importar  io.kotlintest.data.forall
importar  io.kotlintest.shouldBe
importar  io.kotlintest.specs.StringSpec
Estamos  usando  estas  dos  funciones  
adicionales  de  las  bibliotecas  de  KotlinTest.
importar  io.kotlintest.tables.row

clase  OtraPruebaTotal:  StringSpec({
"debería  poder  sumar  3  y  4,  y  no  debe  salir  mal" {
val  totalizador  =  totalizador()

totaler.add(3)  debería  ser  3
totaler.add(4)  debería  ser  7
totaler.total  debe  ser  7
}
Esta  es  la  segunda  prueba.

"debería  poder  sumar  muchos  números  diferentes" {
para  todos(
Ejecutaremos  la  prueba  para  cada  fila  de  datos.
fila  (1,  2,  3),  fila  (19,  
47,  66),  fila  (11,  21,  32)
Los  valores  de  cada  fila  se  asignarán  
a  las  variables  x,  y  y  total  esperado.
) { x,  y,  total  esperado  ­>
val  totaler  =  Totaller(x)  totaler.add(y)  debe   Estas  dos  líneas  se  ejecutarán  para  cada  fila.
ser  esperadoTotal
}
}
})

También  puede  usar  KotlinTest  para:

¥  Ejecutar  pruebas  en  paralelo.

¥  Crear  pruebas  con  propiedades  generadas.

¥ Habilitar/deshabilitar  pruebas  dinámicamente.  Es  posible,  por  ejemplo,  que  desee  que  
algunas  pruebas  se  ejecuten  solo  en  Linux  y  otras  en  Mac.

¥  Poner  pruebas  en  grupos.

y  mucho,  mucho  más.  Si  planea  escribir  mucho  código  Kotlin,  definitivamente  vale  
la  pena  echarle  un  vistazo  a  KotlinTest.

Puede  obtener  más  información  sobre  KotlinTest  aquí:

https://github.com/kotlintest/kotlintest

estas  aqui  4 413
Machine Translated by Google
Machine Translated by Google

apéndice  iii:  sobras

El Arriba Diez Cosas


(Nosotros
No Cubrir)
Madre  mía,  
mira  las  delicias  
que  nos  quedan...

Incluso  después  de  todo  eso,  todavía  hay  un  poco  más.
Hay  algunas  cosas  más  que  creemos  que  necesita  saber.  No  nos  sentiríamos  bien  acerca  de
ignorándolos,  y  realmente  queríamos  darte  un  libro  que  pudieras  levantar  sin  entrenar  
en  el  gimnasio  local.  Antes  de  dejar  el  libro,  lea  estos  datos.

esto  es  un  apéndice  415
Machine Translated by Google

paquetes  e  importaciones

1.  Paquetes  e  importaciones
Como  dijimos  en  el  Capítulo  9,  las  clases  y  funciones  en  la  Biblioteca  
estándar  de  Kotlin  se  agrupan  en  paquetes.  Lo  que  no  dijimos  es  que  
puede  agrupar  su  propio  código  en  paquetes.

Poner  su  código  en  paquetes  es  útil  por  dos  razones  principales:

¥
Te  permite  organizar  tu  código.
Puede  usar  paquetes  para  agrupar  su  código  en  tipos  específicos  de  funcionalidad,  
como  estructuras  de  datos  o  bases  de  datos.

¥
Evita  conflictos  de  nombres.
Si  escribe  una  clase  llamada  Duck,  ponerla  en  un  paquete  le  permite  diferenciarla  de  
cualquier  otra  clase  de  Duck  que  se  haya  agregado  a  su  proyecto.

Cómo  agregar  un  paquete

Para  agregar  un  paquete  a  su  proyecto  de  Kotlin,  resalte  la  
carpeta  src  y  seleccione  Archivo→Nuevo→Paquete.  Cuando  se  
le  solicite,  ingrese  el  nombre  del  paquete  (por  ejemplo,  
com.hfkotlin.mypackage),  luego  haga  clic  en  Aceptar.

Declaraciones  de  paquetes

Cuando  agrega  un  archivo  de  Kotlin  a  un  paquete  (resaltando  el   Este  es  el  nombre  del  

nombre  del  paquete  y  eligiendo  Archivo→Nuevo→Archivo/Clase  de   paquete  que  estamos  creando.
Kotlin),  se  agrega  automáticamente  una  declaración  de  paquete  al  
comienzo  del  archivo  de  origen  como  esta:

paquete  com.hfkotlin.mipaquete
Su  proyecto  puede  contener  
La  declaración  del  paquete  le  dice  al  compilador  que  todo  en  el  
archivo  fuente  pertenece  a  ese  paquete.  El  siguiente  código,  por   varios  paquetes  y  cada  
ejemplo,  especifica  que  com.hfkotlin.mypackage  contiene  la  clase  
Duck  y  la  función  doStuff: paquete  puede  tener  varios  
paquete  com.hfkotlin.mipaquete archivos  de  origen.  Sin  
embargo,  cada  archivo  
pato  de  clase
Este  es  un  archivo  fuente  único,  por  
cosas  divertidas()  {
lo  que  Duck  y  doStuff  se  agregan  al   fuente  solo  puede  tener  una  
...
paquete  com.hfkotlin.mypackage
}
declaración  de  paquete.
Si  el  archivo  de  origen  no  tiene  una  declaración  de  paquete,  el  código  se  
agrega  a  un  paquete  predeterminado  sin  nombre.

416  apéndice  iii
Machine Translated by Google

sobras

El  nombre  completo
Cuando  agrega  una  clase  a  un  paquete,  su  nombre  completo,  o  completamente  
calificado,  es  el  nombre  de  la  clase  con  el  prefijo  del  nombre  del  paquete.  Entonces,  
si  com.hfkotlin.mypackage  contiene  una  clase  llamada  Duck,  el  nombre  completo  
de  la  clase  Duck  es  com.hfkotlin.mypackage.Duck.
Todavía  puede  referirse  a  él  como  Duck  en  cualquier  código  dentro  del  mismo  
paquete,  pero  si  desea  usar  la  clase  en  otro  paquete,  debe  proporcionarle  al  
compilador  su  nombre  completo.

Hay  dos  formas  de  proporcionar  un  nombre  de  clase  completamente  calificado:  
usando  su  nombre  completo  en  todas  partes  de  su  código  o  importándolo. Importaciones  predeterminadas

Escriba  el  nombre  completo... Los  siguientes  paquetes  se  
importan  automáticamente  
La  primera  opción  es  escribir  el  nombre  completo  de  la  clase  cada  vez  que  la   en  cada
use  fuera  de  su  paquete,  por  ejemplo: Archivo  Kotlin  por  defecto:
paquete  com.hfkotlin.myotherpackage kotlin.*
Este  es  un  paquete  diferente.
kotlin.anotación.*
diversión  principal(argumentos:  Array<String>)  {
val  pato  =  com.hfkotlin.mipaquete.Pato() kotlin.colecciones.*

...
Este  es  el  nombre  completamente  calificado. kotlin.comparaciones.*
}
kotlin.io.*
Sin  embargo,  este  enfoque  puede  ser  engorroso  si  necesita  hacer  referencia  a  la  
kotlin.ranges.*
clase  muchas  veces  o  hacer  referencia  a  varios  elementos  en  el  mismo  paquete.
kotlin.secuencias.*

...o  importarlo kotlin.texto.*

Un  enfoque  alternativo  es  importar  la  clase  o  el  paquete  para  que  pueda  hacer   Si  su  plataforma  de  destino  es  la  JVM,  
referencia  a  la  clase  Duck  sin  tener  que  escribir  el  nombre  completo  cada  vez.  Aquí   también  se  importa  lo  siguiente:

hay  un  ejemplo: java.lang.*
paquete  com.hfkotlin.myotherpackage kotlin.jvm.*
Esta  línea  importa  
importar  com.hfkotlin.mypackage.Duck
la  clase  Duck... Y  si  está  apuntando  a  JavaScript,  lo  
siguiente  se  importa  en  su  lugar:
diversión  principal(argumentos:  Array<String>)  {
val  pato  =  pato()
...  para  que  podamos  referirnos  a   kotlin.js.*
...
él  sin  escribir  su  nombre  completo.
}

También  puede  usar  el  siguiente  código  para  importar  un  paquete  completo:

importar  com.hfkotlin.mipaquete.* El  *  significa  "importar  todo  desde  este  paquete".

Y  si  hay  un  conflicto  de  nombre  de  clase,  puede  usar  la  palabra  clave  as :

importar  com.hfkotlin.mypackage.Duck  importar   Aquí,  puede  referirse  a  la  clase  
com.hfKotlin.mypackage2.Duck  como  Duck2 Duck  en  mypackage2  usando  "Duck2".

estas  aqui  4 417
Machine Translated by Google

modificadores  de  visibilidad

2.  Modificadores  de  visibilidad

Los  modificadores  de  visibilidad  le  permiten  establecer  la  visibilidad  de  cualquier  
código  que  cree,  como  clases  y  funciones.  Puede  declarar,  por  ejemplo,  que  
una  clase  solo  puede  ser  utilizada  por  el  código  en  su  archivo  fuente,  o  que  
una  función  miembro  solo  puede  usarse  dentro  de  su  clase.

Kotlin  tiene  cuatro  modificadores  de  visibilidad:  público,  privado,  protegido  e  
interno.  Veamos  cómo  funcionan  estos.

Modificadores  de  visibilidad  y  código  de  nivel  superior

Como  ya  sabe,  el  código  como  las  clases,  las  variables  y  las  funciones  se  pueden   Recuerde:  si  no  especifica  un  
declarar  directamente  dentro  de  un  archivo  o  paquete  fuente.  De  forma   paquete,  el  código  se  agrega  
predeterminada,  todo  este  código  es  visible  públicamente  y  se  puede  usar  en   automáticamente  a  un  paquete  sin  
cualquier  paquete  que  lo  importe.  Sin  embargo,  puede  cambiar  este  comportamiento   nombre  de  forma  predeterminada.
anteponiendo  las  declaraciones  con  uno  de  los  siguientes  modificadores  de  visibilidad:

Modificador:  Qué  hace:
Tenga  en  
público Hace  que  la  declaración  sea  visible  en  todas  partes.  Esto  se  aplica  de  forma  predeterminada,  
por  lo  que  se  puede  omitir. cuenta  que  protected  
no  está  disponible  
privado Hace  que  la  declaración  sea  visible  para  el  código  dentro  de  su  archivo  fuente,  pero  invisible  en   para  declaraciones  
cualquier  otro  lugar.
en  el  nivel  superior  
interno de  un  paquete  o  
Hace  que  la  declaración  sea  visible  dentro  del  mismo  módulo,  pero  invisible  
archivo  fuente.
en  otros  lugares.  Un  módulo  es  un  conjunto  de  archivos  Kotlin  que  se  compilan  
juntos,  como  un  módulo  IntelliJ  IDEA.

El  siguiente  código,  por  ejemplo,  especifica  que  la  clase  Duck  es  pública  y  
se  puede  ver  en  cualquier  lugar,  mientras  que  la  función  doStuff  es  privada  y  
solo  es  visible  dentro  de  su  archivo  fuente:

paquete  com.hfkotlin.mipaquete

Duck  no  tiene  modificador  de  visibilidad,  lo  que  significa  que  es  público.
pato  de  clase

diversión  privada  doStuff()  {
doStuff()  está  marcado  como  privado,  por  lo  que  solo  se  puede  
println("hola") usar  dentro  del  archivo  fuente  donde  está  definido.

Los  modificadores  de  visibilidad  también  se  pueden  aplicar  a  miembros  de  clases  
e  interfaces.  Veamos  cómo  funcionan  estos.

418  apéndice  iii
Machine Translated by Google

sobras

Modificadores  de  visibilidad  y  clases/interfaces
Los  siguientes  modificadores  de  visibilidad  se  pueden  aplicar  a  las  
propiedades,  funciones  y  otros  miembros  que  pertenecen  a  una  clase  o  interfaz:

Modificador:  Qué  hace:

público Hace  que  el  miembro  sea  visible  en  todos  los  lugares  donde  la  clase  es  visible.  Esto  se  aplica  de  forma  
predeterminada,  por  lo  que  se  puede  omitir.

privado Hace  que  el  miembro  sea  visible  dentro  de  la  clase  e  invisible  en  cualquier  otro  lugar.

protected  Hace  que  el  miembro  sea  visible  dentro  de  la  clase  y  cualquiera  de  sus  subclases.
interno Hace  que  el  miembro  sea  visible  para  cualquier  elemento  del  módulo  que  pueda  ver  la  clase.

Aquí  hay  un  ejemplo  de  una  clase  con  modificadores  de  visibilidad  en  sus  
propiedades  y  una  subclase  que  la  anula:

padre  de  clase  abierta  {
var  a  =  1 Como  b  es  privado,  solo  se  puede  usar  dentro  de  esta  clase.  
No  puede  ser  visto  por  ninguna  subclase  de  Parent.
var  privado  b  =  2

abierto  protegido  var  c  =  3
var  interno  d  =  4

La  clase  Child  puede  ver  las  propiedades  a  y  c,  y  también  puede  

clase  Hijo:  Padre()  { acceder  a  la  propiedad  d  si  Parent  y  Child  están  definidos  en  el  mismo  
módulo.  Sin  embargo,  el  niño  no  puede  ver  la  propiedad  b  ya  que  su  
anular  var  c  =  6
modificador  de  visibilidad  es  privado.
}

Tenga  en  cuenta  que  si  anula  un  miembro  protegido,  como  en  el  ejemplo  
anterior,  la  versión  de  subclase  de  ese  miembro  también  estará  protegida  de  forma  
predeterminada.  Sin  embargo,  puede  cambiar  su  visibilidad,  como  en  este  ejemplo:
clase  Hijo:  Padre()  {
La  propiedad  c  ahora  se  puede  ver  en  
anulación  pública  var  c  =  6
cualquier  lugar  donde  la  clase  Child  sea  visible.
}

De  forma  predeterminada,  los  constructores  de  clases  son  públicos,  por  
lo  que  son  visibles  en  todos  los  lugares  donde  la  clase  es  visible.  Sin  
embargo,  puede  cambiar  la  visibilidad  de  un  constructor  especificando  un  modificador  
de  visibilidad  y  prefijando  el  constructor  con  la  palabra  clave  constructor.  Si,  por  
ejemplo,  tiene  una  clase  definida  como:
De  forma  predeterminada,  el  constructor  principal  de  MyClass  es  público.
clase  MiClase(x:  Int)

puede  hacer  que  su  constructor  sea  privado  usando  el  siguiente  código:
constructor  privado  de  clase  MyClass  (x:  Int)

Este  código  hace  que  el  constructor  principal  sea  privado.

estas  aqui  4 419
Machine Translated by Google

enumeraciones

3.  Clases  de  enumeración

Una  clase  de  enumeración  le  permite  crear  un  conjunto  de  valores  que  representan  los  únicos  
valores  válidos  para  una  variable.

Supongamos  que  desea  crear  una  aplicación  para  una  banda  y  desea  asegurarse  de  que  
a  una  variable,  selectedBandMember,  solo  se  le  pueda  asignar  un  valor  para  un  miembro  
válido  de  la  banda.  Para  realizar  este  tipo  de  tarea,  podemos  crear  una  clase  de  
enumeración  llamada  BandMember  que  contenga  los  valores  válidos:

La  clase  enum  tiene  tres  valores:  
enum  class  miembro  de  la  banda  { JERRY,  BOBBY,  PHIL } JERRY,  BOBBY  y  PHIL.

Luego  podemos  restringir  la  variable  selectedBandMember  a  uno  de  estos  valores  

especificando  su  tipo  como  BandMember  así:

diversión  principal(argumentos:  Array<String>)  { El  tipo  de  variable  es  
BandMember... Cada  valor  en  una  clase  de  
var  seleccionadoBandMember:  BandMember

miembroBandMiembroSeleccionado  =  MiembroBanda.JERRY enumeración  es  un
}
...para  que  podamos  
asignarle  uno  de  los  valores  
constante.
Constructores  de  enumeración de  BandMember.

Una  clase  de  enumeración  puede  tener  un  constructor,  que  se  utiliza  para  inicializar  
cada  valor  de  enumeración.  Esto  funciona  porque  cada  valor  definido  por  la  clase  de  
Cada  enumeración
enumeración  es  una  instancia  de  esa  clase.
constante  existe  como
Para  ver  cómo  funciona  esto,  supongamos  que  queremos  especificar  el  instrumento  que  
toca  cada  miembro  de  la  banda.  Para  hacer  esto,  podemos  agregar  una  variable  String  
una  única  instancia  de  esa  
llamada  instrumento  al  constructor  BandMember  e  inicializar  cada  valor  en  la  clase  con  un  
valor  apropiado.  Aquí  está  el  código: clase  de  enumeración.

enum  class  BandMember(val  instrument:  String)  {

JERRY  ("guitarra  solista"),
Esto  define  una  propiedad  llamada  instrumento  en  el  
BOBBY  ("guitarra  rítmica"), constructor  BandMember.  Cada  valor  de  la  clase  de  
PHIL("bajo") enumeración  es  una  instancia  de  BandMember,  por  lo  

} que  cada  valor  tiene  esta  propiedad.

Luego  podemos  averiguar  qué  instrumento  toca  el  miembro  de  la  banda  seleccionado  

accediendo  a  su  propiedad  de  instrumento  de  esta  manera:

diversión  principal(argumentos:  Array<String>)  {
var  seleccionadoBandMember:  BandMember

miembroBandMiembroSeleccionado  =  MiembroBanda.JERRY

println(miembrobandaseleccionado.instrumento) Esto  produce  la  salida  “guitarra  solista”.
}

420  apéndice  iii
Machine Translated by Google

sobras

propiedades  y  funciones  de  enumeración

En  el  ejemplo  anterior,  agregamos  una  propiedad  a  la  clase  BandMember  
incluyéndola  en  el  constructor  de  la  clase  enum.  También  puede  agregar  
propiedades  y  funciones  al  cuerpo  principal  de  la  clase.  El  siguiente  código,  
por  ejemplo,  agrega  una  función  sings  a  la  clase  de  enumeración  
BandMember:

enum  class  BandMember(val  instrument:  String)  {

JERRY  ("guitarra  solista"),

BOBBY  ("guitarra  rítmica"),
Tenga  en  cuenta  que  necesitamos  un  ";"  para  separar  la  función  sings()  de  los  valores  de  enumeración.
PHIL("bajo");

diversión  canta()  =  "ocasionalmente"
Cada  valor  de  enumeración  tiene  una  función  llamada  
}
sings()  que  devuelve  la  cadena  "ocasionalmente".

Cada  valor  definido  en  una  clase  de  enumeración  puede  anular  las  
propiedades  y  funciones  que  hereda  de  la  definición  de  clase.  Así  es  
como,  por  ejemplo,  puede  anular  la  función  de  cantos  para  JERRY  y
POLI:

enum  class  BandMember(val  instrument:  String)  {

JERRY  ("guitarra  solista")  {

anula  los  cantos  divertidos  ()  =  "lastimeramente"

}, JERRY  y  BOBBY  tienen  su  propia  
BOBBY("guitarra  rítmica")  { implementación  de  sings().
anula  los  cantos  divertidos()  =  "roncamente"

},

PHIL("bajo");

open  fun  sings()  =  "ocasionalmente"
Como  estamos  anulando  sings()  para  dos  
} valores,  debemos  marcarlo  como  abierto.

Luego  podemos  averiguar  cómo  canta  el  miembro  de  la  banda  seleccionado  
llamando  a  su  función  sings  de  esta  manera:

diversión  principal(argumentos:  Array<String>)  {
var  seleccionadoBandMember:  BandMember

miembroBandMiembroSeleccionado  =  MiembroBanda.JERRY

println(miembrobandaseleccionado.instrumento)
Esta  línea  llama  a  la  función  sings()  de  JERRY  y  
println(miembro  de  la  banda  seleccionado.  canta())
produce  la  salida  "lastimeramente".
}

estas  aqui  4 421
Machine Translated by Google

sello  o  no  sello

4.  Clases  selladas
Ya  ha  visto  que  las  clases  de  enumeración  le  permiten  crear  un  conjunto  
restringido  de  valores,  pero  hay  algunas  situaciones  en  las  que  necesita  un  poco  
más  de  flexibilidad.

Suponga  que  desea  poder  utilizar  dos  tipos  de  mensajes  diferentes  en  su  
aplicación:  uno  para  "éxito"  y  otro  para  "fracaso".  Desea  poder  restringir  los  
mensajes  a  estos  dos  tipos.

Si  tuviera  que  modelar  esto  usando  una  clase  de  enumeración,  su  código  
podría  verse  así:

enum  class  MessageType(var  msg:  String)  {

ÉXITO  ("¡Sí!"),
La  clase  de  enumeración  MessageType  
FRACASO("¡Buu!") tiene  dos  valores:  SUCCESS  y  FAILURE.
}

Pero  hay  un  par  de  problemas  con  este  enfoque:

¥ Cada  valor  es  una  constante  que  solo  existe  como  una  única  instancia.
Por  ejemplo,  no  puede  cambiar  la  propiedad  msg  del  valor  SUCCESS  en  una  situación,  ya  que  este  
cambio  se  verá  en  todas  partes  de  su  aplicación.

¥ Cada  valor  debe  tener  las  mismas  propiedades  y  funciones.
Puede  ser  útil  agregar  una  propiedad  de  Excepción  al  valor  de  FALLO  para  que  pueda  examinar  qué  
salió  mal,  pero  una  clase  de  enumeración  no  lo  permitirá.

Entonces,  ¿cuál  es  la  solución?

¡Clases  selladas  al  rescate!
Una  solución  a  este  tipo  de  problema  es  utilizar  una  clase  sellada.  Una  clase  
sellada  es  como  una  versión  mejorada  de  una  clase  de  enumeración.  Le  permite  
restringir  su  jerarquía  de  clases  a  un  conjunto  específico  de  subtipos,  cada  uno  de  
los  cuales  puede  definir  sus  propias  propiedades  y  funciones.  Y  a  diferencia  de  una  
clase  de  enumeración,  puede  crear  varias  instancias  de  cada  tipo.

Una  clase  sellada  se  crea  anteponiendo  el  nombre  de  la  clase  con  el  prefijo  sellada.
El  siguiente  código,  por  ejemplo,  crea  una  clase  sellada  denominada  
MessageType,  con  dos  subtipos  denominados  MessageSuccess  y  
MessageFailure.  Cada  subtipo  tiene  una  propiedad  String  denominada  msg,  y  
el  subtipo  MessageFailure  tiene  una  propiedad  Exception  adicional  denominada   MessageSuccess  y  MessageFailure  
e: heredan  de  MessageType  y  definen  
sus  propias  propiedades  en  sus  
clase  sellada  MessageType MessageType  está  sellado. constructores
clase  MessageSuccess(var  msg:  String) :  MessageType()

class  MessageFailure(var  msg:  String,  var  e:  Exception) :  MessageType()

422  apéndice  iii
Machine Translated by Google

sobras

Cómo  usar  las  clases  selladas
Como  dijimos,  una  clase  sellada  le  permite  crear  múltiples  instancias  de  cada  
subtipo.  El  siguiente  código,  por  ejemplo,  crea  dos  instancias  de  MessageSuccess  
y  una  sola  instancia  de  MessageFailure:

diversión  principal(argumentos:  Array<String>)  {
val  mensajeÉxito  =  MensajeÉxito("¡Yay!")

val  mensajeÉxito2  =  MensajeÉxito("¡Funcionó!")

val  messageFailure  =  MessageFailure("¡Boo!",  Exception("Salió  mal."))

A  continuación,  puede  crear  una  variable  MessageType  y  asignarle  uno  de  estos  
mensajes:

diversión  principal(argumentos:  Array<String>)  {
val  mensajeÉxito  =  MensajeÉxito("¡Yay!")

val  mensajeÉxito2  =  MensajeÉxito("¡Funcionó!")

val  messageFailure  =  MessageFailure("¡Boo!",  Exception("Salió  mal."))

messageFailure  es  un  subtipo  de  
var  myMessageType:  MessageType  =  messageFailure
MessageType,  por  lo  que  podemos  
} asignarlo  a  myMessageType.

Y  como  MessageType  es  una  clase  sellada  con  un  conjunto  limitado  de  
subtipos,  puede  usar  when  para  verificar  cada  subtipo  sin  requerir  una  
cláusula  else  adicional  usando  un  código  como  este:

diversión  principal(argumentos:  Array<String>)  {
val  mensajeÉxito  =  MensajeÉxito("¡Yay!")

val  mensajeÉxito2  =  MensajeÉxito("¡Funcionó!")

val  messageFailure  =  MessageFailure("¡Boo!",  Exception("Salió  mal."))

var  myMessageType:  MessageType  =  messageFailure myMessageType  solo  puede  tener  un  tipo  
val  myMessage  =  cuando  (myMessageType)  { de  MessageSuccess  o  MessageFailure,  por  lo  
que  no  hay  necesidad  de  una  cláusula  else  adicional.
es  MessageSuccess  ­>  myMessageType.msg
"  "
es  MessageFailure  ­>  myMessageType.msg  + +  myMessageType.e.message

println(miMensaje)

Puede  obtener  más  información  sobre  la  creación  y  el  uso  de  clases  selladas  aquí:

https://kotlinlang.org/docs/reference/sealed­classes.html

estas  aqui  4 423
Machine Translated by Google

clases  anidadas  e  internas

5.  Clases  anidadas  e  internas
Una  clase  anidada  es  una  clase  que  se  define  dentro  de  otra  clase.  Esto  puede  
ser  útil  si  desea  proporcionar  a  la  clase  externa  una  funcionalidad  adicional  
Una  clase  anidada  
que  está  fuera  de  su  propósito  principal,  o  acercar  el  código  a  donde  se  está  
utilizando.
en  Kotlin  es  como  una  
Una  clase  anidada  se  define  colocándola  dentro  de  las  llaves  de  la  clase  
exterior.  El  siguiente  código,  por  ejemplo,  define  una  clase  llamada  Outer   clase  anidada  estática  en  Java.
que  tiene  una  clase  anidada  llamada  Nested:

clase  exterior  {
val  x  =  "Esto  está  en  la  clase  Exterior"

clase  anidada  {
Esta  es  la  clase  anidada.  Está  
val  y  =  "Esto  está  en  la  clase  anidada"
completamente  encerrado  por  la  clase  exterior.
fun  myFun()  =  "Esta  es  la  función  anidada"
}
}

A  continuación,  puede  consultar  la  clase  anidada  y  sus  propiedades  y  
funciones  mediante  un  código  como  este:

diversión  principal(argumentos:  Array<String>)  {
val  nested  =  Exterior.Anidado()   Crea  una  instancia  de  Nested  y  la  
println(anidado.y)  println(anidado.myFun()) asigna  a  una  variable.

Tenga  en  cuenta  que  no  puede  acceder  a  una  clase  anidada  desde  una  
instancia  de  la  clase  externa  sin  crear  primero  una  propiedad  de  ese  tipo  dentro  
de  la  clase  externa.  El  siguiente  código,  por  ejemplo,  no  se  compilará:

val  anidado  =  Exterior().Anidado() Esto  no  se  compilará  ya  que  estamos  usando  Outer(),  no  Outer.

Otra  restricción  es  que  una  clase  anidada  no  tiene  acceso  a  una  instancia  
de  la  clase  externa,  por  lo  que  no  puede  acceder  a  sus  miembros.  No  puede  
acceder  a  la  propiedad  x  de  Outer  desde  la  clase  anidada,  por  ejemplo,  por  lo  
que  el  siguiente  código  no  se  compilará:
clase  exterior  {
val  x  =  "Esto  está  en  la  clase  Exterior"

clase  anidada  {

divertido  getX()  =  "El  valor  de  x  es:  $x" Nested  no  puede  ver  x  como  está  definido  en  la  
} clase  Outer,  por  lo  que  esta  línea  no  se  compilará.
}

424  apéndice  iii
Machine Translated by Google

sobras

Una  clase  interna  puede  acceder  a  los  miembros  de  la  clase  externa

Si  desea  que  una  clase  anidada  pueda  acceder  a  las  propiedades  y  
funciones  definidas  por  su  clase  externa,  puede  hacerlo  convirtiéndola  en  
una  clase  interna.  Para  ello,  prefije  la  clase  anidada  con  inner.
Aquí  hay  un  ejemplo:
clase  exterior  {
val  x  =  "Esto  está  en  la  clase  Exterior"

clase  interna  interna  {
Una  clase  interna  es  una  clase  anidada  que  
val  y  =  "Esto  está  en  la  clase  interna" tiene  acceso  a  los  miembros  de  la  clase  externa.
fun  myFun()  =  "Esta  es  la  función  interna" Entonces,  en  este  ejemplo,  la  clase  Interior  
divertido  getX()  =  "El  valor  de  x  es:  $x" tiene  acceso  a  la  propiedad  x  de  Exterior.

}
}

Puede  acceder  a  una  clase  interna  creando  una  instancia  de  la  clase  externa  y  
luego  usándola  para  crear  una  instancia  de  la  clase  interna.  Aquí  hay  un  ejemplo,  
usando  las  clases  Outer  e  Inner  definidas  anteriormente:
diversión  principal(argumentos:  Array<String>)  {
val  interior  =  Exterior().Interior() Como  Inner  es  una  clase  interna,  tenemos  que  usar  Outer(),  no  Outer.

println(interior.y)
println(interior.miDiversión())
println(interior.getX())
}

Alternativamente,  puede  acceder  a  la  clase  interna  instanciando  una  propiedad  
de  ese  tipo  en  la  clase  externa,  como  en  este  ejemplo:

clase  exterior  {

val  miInterior  =  Interior()
La  propiedad  myInner  de  
Outer  contiene  una  referencia  
clase  interna  interna  { a  una  instancia  de  su  clase  Inner.
x:  Cadena
...

}
y:  Cadena

} Exterior

Interno
diversión  principal(argumentos:  Array<String>)  {
Los  objetos  Interior  y  Exterior  comparten  un  
val  interior  =  Exterior().miInterior
vínculo  especial.  El  Interior  puede  usar  las  
}
variables  del  Exterior,  y  viceversa.

La  clave  es  que  una  instancia  de  clase  interna  siempre  está  vinculada  a  una  
instancia  específica  de  la  clase  externa,  por  lo  que  no  puede  crear  un  objeto  
interno  sin  crear  primero  un  objeto  externo.

estas  aqui  4 425
Machine Translated by Google

objetos

6.  Declaraciones  y  expresiones  de  objetos
Hay  momentos  en  los  que  desea  asegurarse  de  que  solo  se  pueda  
crear  una  única  instancia  de  un  tipo  determinado,  como  si  desea  usar  
un  solo  objeto  para  coordinar  acciones  en  toda  una  aplicación.  En  estas   Si  está  familiarizado  con  los  
situaciones,  puede  usar  la  palabra  clave  object  para  hacer  una  declaración   patrones  de  diseño,  una  declaración  
de  objeto. de  objeto  es  el  equivalente  Kotlin  de  un  Singleton.

Una  declaración  de  objeto  define  una  declaración  de  clase  y  crea  una  
instancia  de  ella  en  una  sola  declaración.  Y  cuando  lo  incluya  en  el  nivel  
superior  de  un  archivo  o  paquete  fuente,  solo  se  creará  una  instancia  de  
ese  tipo.

Así  es  como  se  ve  una  declaración  de  objeto:

paquete  com.hfkotlin.mipaquete
DuckManager  es  un  objeto.
Una  
objeto  DuckManager  { declaración  de  objeto  
val  allDucks  =  mutableListOf<Duck>()
define  una  clase  y  
Tiene  una  propiedad  llamada  
manada  de  patos  divertidos  ()  { allDucks  y  una  función  llamada  herdDucks(). crea  una  instancia  de  
//Código  para  arrear  los  Patos

}
ella  en  una  sola  declaración.
}

Como  puede  ver,  una  declaración  de  objeto  se  parece  a  una  definición  
de  clase,  excepto  que  tiene  el  prefijo  objeto,  no  clase.
Al  igual  que  una  clase,  puede  tener  propiedades,  funciones  y  bloques  de  
inicialización,  y  puede  heredar  de  clases  o  interfaces.  Sin  embargo,  no  
puede  agregar  un  constructor  a  una  declaración  de  objeto.  Esto  se  debe  a  
que  el  objeto  se  crea  automáticamente  tan  pronto  como  se  accede  a  él,  
por  lo  que  tener  un  constructor  sería  redundante.

Te  refieres  a  un  objeto  que  se  crea  usando  una  declaración  de  objeto  
llamando  directamente  a  su  nombre,  y  esto  te  permite  acceder  a  sus  
miembros.  Si  quisiera  llamar  a  la  función  herdDucks  de  DuckManager,  por  
ejemplo,  podría  hacerlo  usando  un  código  como  este:

DuckManager.herdDucks()

Además  de  agregar  una  declaración  de  objeto  al  nivel  superior  de  un  
paquete  o  archivo  fuente,  también  puede  agregar  uno  a  una  clase.  Veamos  
cómo.

426  apéndice  iii
Machine Translated by Google

sobras

Objetos  de  clase...
El  siguiente  código  agrega  una  declaración  de  objeto,  DuckFactory,  a  una  clase  
denominada  Duck:

pato  de  clase  { La  declaración  del  objeto  va  en   Agregue  una  


el  cuerpo  principal  de  la  clase.
objeto  DuckFactory  {
declaración  de  
divertido  crear():  Pato  =  Pato()
} objeto  a  una  clase  
}

Cuando  agrega  una  declaración  de  objeto  a  una  clase,  crea  un  objeto  que  pertenece  a  
para  crear  una  única  
esa  clase.  Se  crea  una  instancia  del  objeto  por  clase  y  es  compartida  por  todas  las  
instancias  de  esa  clase.
instancia  de  ese  tipo  
Una  vez  que  haya  agregado  una  declaración  de  objeto,  puede  acceder  al  objeto  desde  la   que  pertenezca  a  la  clase.
clase  usando  la  notación  de  puntos.  El  siguiente  código,  por  ejemplo,  llama  a  la  función  de  
creación  de  DuckFactory  y  asigna  el  resultado  a  una  nueva  variable  llamada  newDuck:

val  newDuck  =  Duck.DuckFactory.create() Tenga  en  cuenta  que  accede  
al  objeto  utilizando  Duck,  no  Duck().
...y  objetos  complementarios
Se  puede  marcar  un  objeto  por  clase  como  objeto  complementario  mediante  el  prefijo  
complementario .  Un  objeto  complementario  es  como  un  objeto  de  clase,  excepto  que  puede  
omitir  el  nombre  del  objeto.  El  siguiente  código,  por  ejemplo,  convierte  el  objeto  DuckFactory  
anterior  en  un  objeto  complementario  sin  nombre:

pato  de  clase  {
objeto  complementario  DuckFactory  {
Sin  embargo,  puede  incluir  el  
Si  coloca  el  prefijo  acompañante  en  una  declaración  de  
objeto,  ya  no  necesita  proporcionar  un  nombre  de  objeto.
divertido  crear():  Pato  =  Pato() nombre  si  lo  desea.
}
}

Cuando  crea  un  objeto  complementario,  accede  a  él  simplemente  haciendo  
Se  puede  usar  un  objeto  
referencia  al  nombre  de  la  clase.  El  siguiente  código,  por  ejemplo,  llama  a  la   complementario  como  el  
función  create()  que  está  definida  por  el  objeto  complementario  de  Duck:

equivalente  de  Kotlin  a  los  métodos  
val  nuevoPato  =  Pato.create()
estáticos  en  Java.
Para  obtener  una  referencia  a  un  objeto  complementario  sin  nombre,  
utilice  la  palabra  clave  Companion.  El  siguiente  código,  por  ejemplo,  crea   Todas  las  instancias  de  clase  
una  nueva  variable  llamada  x  y  le  asigna  una  referencia  al  objeto  complementario  
de  Duck: comparten  todas  las  funciones  que  
val  x  =  Pato.Compañero
agrega  a  un  objeto  complementario.
Ahora  que  ha  aprendido  acerca  de  las  declaraciones  de  objetos  y  
los  objetos  complementarios,  veamos  las  expresiones  de  objetos.

estas  aqui  4 427
Machine Translated by Google

expresiones  de  objeto

Expresiones  de  objetos

Una  expresión  de  objeto  es  una  expresión  que  crea  un  objeto  anónimo  sobre  la  
marcha  sin  un  tipo  predefinido.

Suponga  que  desea  crear  un  objeto  que  contenga  un  valor  inicial  para  las  
coordenadas  x  e  y.  En  lugar  de  definir  una  clase  de  coordenadas  y  crear  una  instancia  
de  la  misma,  podría  crear  un  objeto  que  use  propiedades  para  contener  los  valores  de  
las  coordenadas  x  e  y.  El  siguiente  código,  por  ejemplo,  crea  una  nueva  variable  
denominada  punto  de  partida  y  le  asigna  dicho  objeto:

val  punto  de  partida  =  objeto  {
valor  x  =  0

valor  y  =  0 Esto  crea  un  objeto  con  dos  
propiedades,  x  e  y.
}

Luego  puede  referirse  a  los  miembros  del  objeto  usando  un  código  como  este:

println("el  punto  de  inicio  es  ${puntoInicio.x},  ${puntoInicio.y}")

Las  expresiones  de  objetos  se  utilizan  principalmente  como  el  equivalente  de  las  
clases  internas  anónimas  en  Java.  Si  está  escribiendo  código  GUI  y  de  repente  se  
da  cuenta  de  que  necesita  una  instancia  de  una  clase  que  implemente  una  clase  
abstracta  MouseAdapter,  puede  usar  una  expresión  de  objeto  para  crear  esa  instancia  
sobre  la  marcha.  El  siguiente  código,  por  ejemplo,  pasa  un  objeto  a  una  función  
llamada  addMouseListener;  el  objeto  implementa  MouseAdapter  y  anula  sus  funciones  
mouseClicked  y  mouseEntered:

La  expresión  del  objeto  en  
Esta   negrita  es  como  decir  "cree  
ventana.addMouseListener(objeto :  MouseAdapter()  {
declaración... una  instancia  de  una  clase  
anular  diversión  mouseClicked  (e:  MouseEvent)  {
(sin  nombre)  que  implemente  
//Código  que  se  ejecuta  cuando  se  hace  clic  con  el  mouse
MouseAdapter  y,  por  cierto,  
} aquí  está  la  implementación  
de  las  funciones  mouseClicked  
y  mouseReleased".  En  otras  
anula  la  diversión  mouseReleased  (e:  MouseEvent)  {
palabras,  estamos  
//Código  que  se  ejecuta  cuando  se  suelta  el  mouse
...termina   proporcionando  la  función  
aquí   } addMouseListener  con  una  
abajo. }) implementación  de  clase  y  una  
instancia  de  esa  clase,  justo  
donde  la  necesitamos.
Puede  encontrar  más  información  sobre  declaraciones  y  expresiones  de  objetos  aquí:

https://kotlinlang.org/docs/reference/object­declarations.html

428  apéndice  iii
Machine Translated by Google

sobras

7.  Extensiones
Las  extensiones  le  permiten  agregar  nuevas  funciones  y  propiedades  a  un   También  hay  bibliotecas  de  extensión  de  Kotlin  
tipo  existente  sin  tener  que  crear  un  subtipo  completamente  nuevo.
que  puede  usar  para  facilitar  su  vida  de  
Imagine  que  está  escribiendo  una  aplicación  en  la  que  con  frecuencia  necesita   codificación,  como  Anko  y  Android  KTX  para  el  
prefijar  un  Doble  con  "$"  para  formatearlo  como  dólares.  En  lugar  de  realizar  la   desarrollo  de  aplicaciones  de  Android.
misma  acción  una  y  otra  vez,  puede  escribir  una  función  de  extensión  llamada  
toDollar  que  puede  usar  con  Doubles.  Aquí  está  el  código  para  hacer  esto:

Define  una  función  denominada  toDollar(),  que  amplía  Double.

diversión  Double.toDollar():  Cadena  {

devolver  "$$esto"
Devuelve  el  valor  actual,  con  el  prefijo  $.
}

El  código  anterior  especifica  que  una  función  llamada  toDollar,  que  
devuelve  una  cadena,  se  puede  usar  con  valores  dobles.  La  función  toma  
el  objeto  actual  (referido  al  uso  de  this),  lo  prefija  con  "$"  y  devuelve  el  
resultado.

Una  vez  que  haya  creado  una  función  de  extensión,  puede  usarla  de  la  
Patrones  de  diseño
misma  manera  que  usaría  cualquier  otra  función.  El  siguiente  código,  por   Los  patrones  de  diseño  
ejemplo,  llama  a  la  función  toDollar  en  una  variable  Double  que  tiene  un  valor   son  soluciones  de  propósito  
de  45,25: general  para  problemas  
comunes,  y  Kotlin  te  ofrece  
var  doble  =  45,25 formas  sencillas  de  implementar  
println(dbl.toDollar()) //imprime  $45.25 algunos  de  estos  patrones.

Las  declaraciones  de  objetos  proporcionan  
Puede  crear  propiedades  de  extensión  de  forma  similar  a  como  crea   una  forma  de  implementar  el  patrón  
funciones  de  extensión.  El  siguiente  código,  por  ejemplo,  crea  una  propiedad   Singleton ,  ya  que  cada  declaración  crea  
de  extensión  para  cadenas  denominada  halfLength  que  devuelve  la  longitud   una  sola  instancia  de  ese  objeto.
de  la  cadena  actual  dividida  por  2,0: Las  extensiones  se  pueden  usar  en  
lugar  del  patrón  Decorator ,  ya  que  
val  String.halfLength le  permiten  ampliar  el  comportamiento  
Define  una  propiedad  halfLength  que  
obtener  ()  =  longitud /  2.0 se  puede  usar  con  Strings. de  las  clases  y  los  objetos.  Y  si  está  
interesado  en  utilizar  el  patrón  
Y  aquí  hay  un  código  de  ejemplo  que  usa  la  nueva  propiedad: Delegación  como  alternativa  a  la  
herencia,  puede  obtener  más  
val  prueba  =  "Esto  es  una  prueba" información  aquí:
println(prueba.halfLength) //  imprime  7.0 https://kotlinlang.org/docs/reference/
delegation.html
Puede  obtener  más  información  sobre  cómo  usar  las  extensiones,  incluido  
cómo  agregarlas  a  los  objetos  complementarios,  aquí:

https://kotlinlang.org/docs/reference/extensions.html

Y  puede  obtener  más  información  sobre  el  uso  de  esto  aquí:

https://kotlinlang.org/docs/reference/this­expressions.html

estas  aqui  4 429
Machine Translated by Google

saltando  _

8.  Vuelve,  rompe  y  continúa
Kotlin  tiene  tres  formas  de  salir  de  un  bucle.  Estos  son:

¥ return  
Como  ya  sabes,  esto  regresa  de  la  función  envolvente.

¥ romper
Esto  termina  (o  salta  al  final  de)  el  bucle  envolvente,  por  ejemplo:
var  x  =  0

var  y  =  0  
mientras  (x  <  10)  {
x++ Este  código  incrementa  x,  luego  termina  el  
romper
bucle  sin  ejecutar  la  línea  y++.  x  tiene  un  
y++ valor  final  de  1  y  el  valor  de  y  sigue  siendo  0.
}

¥ continuar
Esto  pasa  a  la  siguiente  iteración  del  bucle  envolvente,  por  ejemplo:
var  x  =  0

var  y  =  0
mientras  (x  <  10)  {
x++
Esto  incrementa  x,  luego  vuelve  a  la  línea  while  (x  <  10)  sin  ejecutar  
continuar
la  línea  y++.  Sigue  incrementando  x  hasta  que  la  condición  while  
y++ (x  <  10)  es  falsa.  x  tiene  un  valor  final  de  10  y  el  valor  de  y  sigue  
} siendo  0.

Usar  etiquetas  con  pausa  y  continuar
Si  tiene  bucles  anidados,  puede  especificar  explícitamente  de  qué  bucle  
desea  salir  prefijándolo  con  una  etiqueta.  Una  etiqueta  se  compone  de  un  
nombre,  seguido  del  símbolo  @.  El  siguiente  código,  por  ejemplo,  presenta  
dos  bucles,  donde  un  bucle  está  anidado  dentro  de  otro.  El  ciclo  externo  
tiene  una  etiqueta  denominada  myloop@,  que  se  usa  en  una  expresión  de  ruptura:
myloop@  while  (x  <  20)  {
mientras  (y  <  20)  {
x++

romper@myloop Esto  es  como  decir  "salir  del  bucle  
} etiquetado  como  myloop@  (el  bucle  exterior)".
}

Cuando  usa  break  con  una  etiqueta,  salta  al  final  del  ciclo  envolvente  con  esta  
etiqueta,  por  lo  que  en  el  ejemplo  anterior,  termina  el  ciclo  externo.  Cuando  
usa  continuar  con  una  etiqueta,  salta  a  la  siguiente  iteración  de  ese  ciclo.

430  apéndice  iii
Machine Translated by Google

sobras

Uso  de  etiquetas  con  retorno
También  puede  usar  etiquetas  para  controlar  el  comportamiento  de  su  código  en  
funciones  anidadas,  incluidas  las  funciones  de  orden  superior.

Suponga  que  tiene  la  siguiente  función,  que  incluye  una  llamada  a  forEach,  que  es  
una  función  integrada  de  orden  superior  que  acepta  una  lambda:

diversión  miDiversión()  {

lista("A",  "B",  "C",  "D").paraCada  {
si  (es  ==  "C")  volver
Aquí,  estamos  usando  return  dentro  de  una  lambda.  
imprimir  (es)
Cuando  llegamos  al  retorno,  sale  de  la  función  myFun().
}

println("Terminó  miDiversión()")
}

En  este  ejemplo,  el  código  sale  de  la  función  myFun  cuando  llega  a  la  expresión  de  retorno,  
por  lo  que  la  línea:

println("Terminó  miDiversión()")

nunca  corre

Si  desea  salir  de  la  lambda  pero  continuar  ejecutando  myFun,  puede  agregar  una  etiqueta  a  
la  lambda,  a  la  que  luego  puede  hacer  referencia  el  retorno.
Aquí  hay  un  ejemplo:

diversión  miDiversión()  {

listOf("A",  "B",  "C",  "D").forEach  myloop@{ if  (it  ==  "C")  return@myloop
La  lambda  que  estamos  pasando  a  la  función  forEach  
imprimir  (es) está  etiquetada  como  myloop@.  La  expresión  de  retorno  
} de  lambda  usa  esta  etiqueta,  por  lo  que  cuando  se  
println("Terminó  miDiversión()") alcanza,  sale  de  lambda  y  regresa  a  su  llamador  (el  bucle  

} forEach).

Esto  se  puede  reemplazar  con  una  etiqueta  implícita ,  cuyo  nombre  coincida  con  la  función  a  
la  que  se  pasa  la  lambda:

diversión  miDiversión()  {

lista("A",  "B",  "C",  "D").paraCada  {
si  (es  ==  "C")  devuelve  @  para  cada  uno
Aquí,  estamos  usando  una  etiqueta  implícita  
imprimir  (es) para  decirle  al  código  que  salga  de  la  lambda  y  
} regrese  a  su  llamador  (el  bucle  forEach).
println("Terminó  miDiversión()")
}

Puede  encontrar  más  información  sobre  cómo  usar  etiquetas  para  controlar  sus  saltos  de  
código  aquí:

https://kotlinlang.org/docs/reference/returns.html

estas  aqui  4 431
Machine Translated by Google

más  cosas  de  función

9.  Más  diversión  con  funciones
Ha  aprendido  mucho  sobre  funciones  a  lo  largo  del  libro,  pero  hay  
algunas  cosas  más  que  pensamos  que  debería  saber.

Vararg
Si  desea  que  una  función  acepte  múltiples  argumentos  del  mismo  
tipo  pero  no  sabe  cuántos,  puede  prefijar  el  parámetro  con  vararg.  
Esto  le  dice  al  compilador  que  el  parámetro  puede  aceptar  un  número  
Solo  se  
variable  de  argumentos.  Aquí  hay  un  ejemplo:
puede  marcar  
El  prefijo  vararg  significa  que  podemos   un  parámetro  
pasar  múltiples  valores  para  el  parámetro  ints.

diversión  <T>  valoresToList(vararg  vals:  T):  MutableList<T>  {
con  vararg.  Este  
lista  de  valores:  MutableList<T>  =  mutableListOf()
parámetro  suele  
para  (i  en  vals)  {

lista.añadir(i)
ser  el  último.
Los  valores  vararg  se  pasan  a  la  función  como  una  
}
matriz,  por  lo  que  podemos  recorrer  cada  valor.  Aquí,  
lista  de  retorno
estamos  agregando  cada  valor  a  MutableList.
}

Usted  llama  a  una  función  con  un  parámetro  vararg  pasándole  valores,  
tal  como  lo  haría  con  cualquier  otro  tipo  de  función.  El  siguiente  código,  
por  ejemplo,  pasa  cinco  valores  Int  a  la  función  valuesToList:

val  mList  =  valoresEnLista(1,  2,  3,  4,  5)

Si  tiene  una  matriz  de  valores  existente,  puede  pasarlos  a  la  
función  anteponiendo  el  nombre  de  la  matriz  con  *.  Esto  se  conoce  
como  el  operador  de  propagación,  y  aquí  hay  un  par  de  ejemplos  en  uso:
val  miArray  =  arrayOf(1,  2,  3,  4,  5) Esto  pasa  los  valores  contenidos  en  myArray  
a  la  función  de  valores  a  la  lista.
val  mList  =  valoresEnLista(*miArray)

val  mList2  =  valoresEnLista(0,  *miArray,  6,  7)
Pasar  0  a  la  función... ...seguido  por  6  y  7.
...seguido  
por  el  
contenido  de  myArray...

432  apéndice  iii
Machine Translated by Google

sobras

infijo

Si  antepone  una  función  con  infijo,  puede  llamarla  sin  usar  la  notación  de  
punto.  Aquí  hay  un  ejemplo  de  una  función  infija:

perro  de  clase  {
Hemos  
infijo  ladrido  divertido  (x:  Int):  String  {
marcado  
la  función   //Código  para  hacer  que  el  Perro  ladre  x  veces

bark()  con  infix. }
}

Como  la  función  se  ha  marcado  con  un  infijo,  puede  llamarla  usando:

Ladrido  de  perro()  6
Esto  crea  un  Perro  y  llama  a  su  función  
ladrar(),  pasando  a  la  función  un  valor  de  6.

Una  función  se  puede  marcar  con  infijo  si  es  una  función  miembro  o  de  extensión  
y  tiene  un  solo  parámetro  que  no  tiene  un  valor  predeterminado  y  no  está  
marcada  con  vararg.

en  línea

Las  funciones  de  orden  superior  a  veces  pueden  ser  un  poco  más  lentas  de  
ejecutar,  pero  la  mayoría  de  las  veces,  puede  mejorar  su  rendimiento  al  
anteponer  la  función  con  el  prefijo  en  línea,  por  ejemplo:
diversión  en  línea  convertir  (x:  Doble,  convertidor:  (Doble)  ­>  Doble) :  Doble  {

val  resultado  =  convertidor(x) Esta  es  una  función  que  creamos  
println("$x  se  convierte  en  $resultado") en  el  Capítulo  11,  pero  aquí  la  
resultado  devuelto hemos  marcado  como  una  función  en  línea.

Cuando  inserta  una  función  de  esta  manera,  el  código  generado  elimina  la  
llamada  a  la  función  y  la  reemplaza  con  el  contenido  de  la  función.  Elimina  la  
sobrecarga  de  llamar  a  la  función,  lo  que  a  menudo  hará  que  el  código  se  ejecute  
más  rápido,  pero  detrás  de  escena  genera  más  código.

Puede  encontrar  información  adicional  sobre  el  uso  de  estas  técnicas,  y  más,  
aquí:

https://kotlinlang.org/docs/reference/functions.html

estas  aqui  4 433
Machine Translated by Google

interoperabilidad

10.  Interoperabilidad
Como  dijimos  al  comienzo  del  libro,  Kotlin  es  interoperable  con  Java  y  el  código  de  
Kotlin  se  puede  transpilar  a  JavaScript.  Si  planea  usar  su  código  Kotlin  con  otros  
idiomas,  le  recomendamos  que  lea  las  secciones  de  interoperabilidad  de  la  
documentación  en  línea  de  Kotlin.

Interoperabilidad  con  Java
Puede  llamar  a  casi  todo  el  código  Java  desde  Kotlin  sin  ningún  problema.
Simplemente  importe  las  bibliotecas  que  no  se  hayan  importado  automáticamente  y  
utilícelas.  Puede  leer  sobre  cualquier  consideración  adicional,  como  la  forma  en  que  
Kotlin  trata  los  valores  nulos  provenientes  de  Java,  aquí:  https://kotlinlang.org/docs/

reference/java­interop.html  De  manera  similar,  puede  obtener  más  información  sobre  

el  uso  de  Kotlin  código  desde  dentro  de  Java  aquí:  https://kotlinlang.org/docs/reference/
java­to­kotlin­interop.html

Usando  Kotlin  con  JavaScript
La  documentación  en  línea  también  incluye  una  gran  cantidad  de  información  sobre  
el  uso  de  Kotlin  con  JavaScript.  Si  su  aplicación  tiene  como  objetivo  JavaScript,  por  
ejemplo,  puede  usar  el  tipo  dinámico  de  Kotlin  que  desactiva  efectivamente  el  verificador  
de  tipos  de  Kotlin:

val  miVariableDinámica:  dinámica  = ...

Puede  encontrar  más  información  sobre  el  tipo  dinámico  aquí:

https://kotlinlang.org/docs/reference/dynamic­type.html
Si  desea  
De  manera  similar,  la  siguiente  página  le  brinda  información  sobre  el  uso su  
poder  
múltiples  
código  
compartir  
en  
JavaScript  de  Kotlin: destino,  
plataformas  
le   de  
https://kotlinlang.org/docs/reference/js­interop.html consulte  
sugerimos  
el  que  
Y  puede  obtener  información  sobre  cómo  acceder  a  su  código  Kotlin  desde  JavaScript  
Kotlin  
soporte  
para  
de  proyectos  mult
aquí: información  
Puede  obtener  
sobre
más  
https://kotlinlang.org/docs/reference/js­to­kotlin­interop.html

Escribiendo  código  nativo  con  Kotlin proyectos  multiplataforma  aquí:  https://kotlinlang.org/docs/reference/multiplatform.html

También  puede  usar  Kotlin/Native  para  compilar  código  Kotlin  en  binarios  
nativos.  Para  obtener  más  información  sobre  cómo  hacer  esto,  consulte  aquí:

https://kotlinlang.org/docs/reference/native­overview.html

434  apéndice  iii
Machine Translated by Google

Índice
()  (paréntesis)  

simbolos argumentos  y  13  
expresiones  booleanas  y  82  
&&  (y  operador)  81,  182,  224  @   parámetros  lambda  y  343  
(anotación/etiqueta)  411,  430–431 //   constructores  de  superclase  y  173 ..  
(comentario)  13,  16  {}  (llaves)  cuerpo  de   (operador  de  rango)  76–77  ===  (operador  
clase  y  94  cuerpo  de  función  vacío  y  160   de  igualdad  referencial)  200,  265–267 ?.  (operador  de  
interfaces  y  171  lambdas  y  327  let   llamada  segura)  225,  231–232  ­>  (separador)  327 ,  
cuerpo  y  232  función  principal  y  13  
(separador)  65  *  (operador  de  propagación)  432  $  (plantilla  
clases  anidadas  y  424  Plantillas  de  
cadenas  y  48  ­­  (operador  decremento)   de  cadena)  48
76 .  (operador  de  punto)  40,  96,  114,  
144 ?:  (operador  de  Elvis)  233  ==  
(operador  de  igualdad)  sobre  17,  
200,  267,  271  función  equals()  y  192,  
194–196,  265–266  funciones  generadas  
A
y  205  =  (operador  igual)  17  <>  (genéricos)   clases  abstractas
sobre  158–161  
49,  290  >  (operador  mayor  que)  17  >=  
declaración  157  
(operador  mayor  o  igual  que)  17  ++  
implementación  162–163  
(operador  de  incremento)  76,  430  <   herencia  y  162  creación  de  
(operador  menor  que)  17  <=  (operador  menor  o  igual   instancias  y  157  consejos  
que)  17 :  (separador  de  nombre/tipo)  37,  135,  162,  
al  crear  175  funciones  
173 !=  (operador  no  igual)  82,  224 !!  (operador  de  aserción   abstractas  sobre  159–160,  175  
no  nulo)  234 !  (no  operador)  82,  182 ?  (tipo  anulable)   clases  concretas  y  173  
222–223  ||  (u  operador)  81,  182 implementación  162–163  
interfaces  y  171  polimorfismo  
y  161  palabra  clave  abstracta  
157,  159,  171  resumen  
propiedades

alrededor  de  159­160
clases  concretas  y  173  
implementando  162–163  
inicialización  y  159,  162  
polimorfismo  y  161  superclases  
abstractas  157,  162–163  accesores  
(getters)  111–113,  137,  163,  172

este  es  el  indice  435
Machine Translated by Google

el  índice

acciones  411 atributos  de  ejecución  asincrónica  402–404  

función  agregarTodo() (objetos).  Ver  propiedades  función  promedio()  
Interfaz  MutableList  259
(Array)  252
Interfaz  MutableSet  268

función  agregar  ()
Interfaz  MutableList  257 B
Interfaz  MutableSet  268 campos  de  respaldo  113,  172  
y  operador  (&&)  81,  182,  224 clases  base.  Ver  comportamiento  de  
dispositivos  Android  3
superclases  (objetos)  40,  125,  127.  Ver  también  funciones  números  
corchetes  angulares  <>  49,  290   binarios  35  Expresiones  booleanas  81–82  Pruebas  booleanas,  
anotaciones/etiquetas  (@)  411,  430–431  Cualquier   simples  17  Tipo  booleano  36
superclase  193–194,  196,  202,  223  aplicaciones,  

construcción.  Ver  aplicaciones  de  construcción

argumentos   declaración  de  ruptura  430
sobre  13,  64  
creación  de  aplicaciones  
nombrado  208  y  
adición  de  archivos  a  proyectos  11–12  
orden  de  parámetros  65  funciones  de  
adición  de  funciones  13–16  descripción  
sobrecarga  211 general  básica  4
Clase  de  matriz  45,  77,  252–253,  271   construir  herramientas  7

función  arrayListOf()  282  función  arrayOf()   creación  de  proyectos  8–12  
instalación  del  código  de  
45,  63,  146,  252–253  función  arrayOfNulls()  252
prueba  IDE  7  con  REPL  4,  23–24  funciones  
de  actualización  17–21  funciones  integradas  
matrices  
de  orden  superior  sobre  363–364  función  filter()  
creando  aplicaciones  utilizando  46–47   364,  371–374  función  filterIsInstance()  371  
creando  45,  63  declarando  50–51  evaluando  
filterNot()  función  371,  374  filterTo()  función  
48  definiendo  explícitamente  el  tipo  49  
371,  374  fold()  función  383–386,  389  
infiriendo  el  tipo  a  partir  de  valores  49  
foldRight()  función  389  forEach()  función  
limitaciones  de  253  recorriendo  elementos  
375–376,  382,  389  groupBy()  función  381–
en  77  de  tipos  anulables  223,  253  referencias  
382  map()  función  372–374  función  maxBy()  
a  objetos  y  45,  49–51,  69  –71  valor  de  índice  
365–366  función  max()  365–366  función  
inicial  45  almacenar  valores  en  45  formas  de  
minBy()  365–366  función  min()  365–366  función  
usar  252
reduceRight()  389  función  sumByDouble()  367  
función  sumBy()  367

Array<Type>  tipo  49  como  

operador  185,  243,  417  aserción  

Equals  aserción  411  operadores  

de  aserción  234  aserciones  411
Tipo  de  byte  35

operadores  de  asignación  17

436  índice
Machine Translated by Google

el  índice

Colección  interfaz  389
C colecciones
alrededor  de  
función  capitalize()  88  
282  matrices  y  252–253  
conversión  184–185,  242–243  
genéricos  y  290–293,  297–300  funciones  
atrapar  bloque  (intentar/atrapar)  240–241,  244   de  orden  superior  y  364–367,  371–376,  381–386,  389

atrapar  excepciones  239–240  características  
Biblioteca  estándar  de  Kotlin  254
(objetos).  Ver  propiedades  caracteres  (tipo)  36  
Interfaz  de  lista  255–256,  263,  271
Char  type  36  ClassCastException  242–243 Interfaz  de  mapa  255,  276–280
Interfaz  MutableList  255,  257–259,  263,  291–293,
300
Interfaz  MutableMap  255,  278–280,  297
clases Interfaz  MutableSet  255,  264,  268–269,  298–299
acerca  de  91–92   Establecer  interfaz  255,  264–269,  271,  282  dos  
resumen  157–163  
puntos  (:)  37,  135,  162,  173  coma  (,)  65  comentarios,  
adición  a  proyectos  133,  143  
barra  inclinada  y  13,  16  palabra  clave  complementaria  
construcción  133–140  protocolos  
comunes  para  145,  150,  156,  161,  171  datos  concretos  158,   427
163,  173,  300.  Consulte  clases  de  datos  que  definen  92–94  
que  definen  propiedades  en  el  cuerpo  principal  106  que  
Interfaz  comparable  366  operadores  
definen  sin  constructores  109  que  diseñan  93  enum  420–421  
de  comparación  17,  192,  194–196,  200  funciones  de  
genéricos  y  293–296,  302,  308,  313,  315  herencia.  Véase  
herencia  funciones  miembro  internas  425  y  94,  96  anidadas   componenteN  199  clases  concretas  158,  163,  173,  305
424–425  externas  424–425  prefijando  con  subclases  abiertas  
134,  137  selladas  422–423.  Ver  subclases  superclases.  Ver  
funciones  concretas  171
superclases  como  plantillas  91–92,  95,  175  consejos  al  crear  
175  modificadores  de  visibilidad  y  419  palabra  clave  de  clase   bifurcación  condicional  if  
94  Función  clear()  Interfaz  MutableList  259  Interfaz   expresión  20,  48,  67,  233  if  declaración  
MutableMap  279  Interfaz  MutableSet  268 17–19  función  principal  usando  16

pruebas  condicionales  17–18

configurar  proyectos  10
constantes
clases  enum  y  420–421  clases  
selladas  y  422–423

palabra  clave  del  constructor  209
constructores
acerca  de  99–102  
con  valores  predeterminados  207–
208  definiendo  clases  sin  109  
definiendo  propiedades  100,  102,  205  vacío  
109  clases  de  enumeración  y  420  genéricos  
y  314
cierre  (lambdas)  376,  389  editores  

de  código  7,  14
@JvmOverloads  anotación  214

estas  aqui  4 437
Machine Translated by Google

el  índice

D
primario.  Ver  constructores  primarios  
secundarios  209,  214  modificadores  de  
visibilidad  419  función  contiene  ()  Clase  
clases  de  datos
de  matriz  252  Interfaz  de  lista  256  Interfaz  de  
acerca  de  196,  200,  202  
conjunto  264
componentesN  funciones  y  199  
constructores  con  valores  predeterminados  207–
208  copiar  objetos  de  datos  198  crear  objetos  a  
función  containsKey()  (mapa)  277  función  
partir  de  196  definir  196,  205  funciones  generadas  
containsValue()  (mapa)  277 y  205  inicializar  muchas  propiedades  206  
continuar  declaración  430 sobrecargar  funciones  211  anular  el  
comportamiento  heredado  197,  202  parámetros  
tipos  genéricos  contravariantes  315–316,  319  
con  valores  predeterminados  210,  214  
funciones  de  conversión  40
constructores  primarios  205–209  reglas  para  217  
conversión  de  valores  40–42   constructores  secundarios  209  ocultación  de  
datos  114  palabra  clave  de  datos  196  copia  de  
función  copy()  198,  202
objetos  de  datos  198  creación  196  desestructuración  
corrutinas
199  propiedades  y  197  declaraciones
agregando  dependencias  402–403  
ejecución  asíncrona  402  aplicación  
de  caja  de  ritmos  398–408  lanzando  402–
406  función  runBlocking()  405  subprocesos  
y  404–406

tipos  genéricos  covariantes  308,  319  
creación  de  clases  abstractas  175  
matrices  45,  63  excepciones  242  
clases  abstractas  157  
funciones  64  interfaces  175  objetos  
95,  98–100,  196  proyectos  4,  8– arreglos  50–51  clases  
12,  62  subclases  175  variables  32 134  funciones  66  objeto  
98,  426–427,  429  
paquetes  416  pasar  valores  
en  orden  de  207  propiedades  
112  superclases  134,  139  variables  
32–37,  98,  331

llaves  {}  cuerpo  
de  clase  y  94  cuerpo  
de  función  vacío  y  160  interfaces  y   Patrón  decorador  429  
171 operador  decremento  (­­)  76
lambdas  y  327  cuerpo   constructores  
let  y  232  función   de  valores  predeterminados  con  
principal  y  13  clases  
207–208  parámetros  con  210,  
anidadas  y  424
214  propiedades  con  206  función  
Plantillas  de  cadenas  y  48  
delay()  406
getters/setters  personalizados  112
Patrón  de  delegación  429
clases  derivadas.  Ver  subclases

438  índice
Machine Translated by Google

el  índice

patrones  de  diseño  429   IllegalArgumentException  242,  244  

desestructuración  de  objetos  de  datos   IllegalStateException  242  NullPointerException  
221,  234  reglas  para  244  lanzar  234,  239,  
199  signo  de  dólar  ($)  48  operador  de   244  bloque  try/catch  240  Tipo  de  excepción  
punto  (.)  40,  96,  114,  144  Tipo  doble  35   242  conversión  explícita  185,  243  declarar  
bucles  do­while  17  función  downTo()  77   explícitamente  variables  37  definir  

código  duplicado,  evitando  122,  125   explícitamente  el  tipo  de  matriz  49  lanzar  

valores  duplicados  Interfaz  de  lista  y   explícitamente  excepciones  244  expresiones  

263  Interfaz  de  mapa  y  276,  280  Interfaz   booleanas  81–82  encadenar  llamadas  seguras  

de  configuración  y  255,  265,  269 juntas  226–227  si  20,  48,  67,  233  lambda.  Ver  

objetos  lambdas  428  valores  devueltos  y  245  

shouldBe  412  simplificación  con  let  232  Plantillas  
de  cadenas  que  evalúan  48  extensiones  429

mi
otra  cláusula

si  expresión  20  si  
declaración  19  
cuando  declaración  183

Operador  Elvis  (?:)  233  

constructores  vacíos  109  

F
cuerpo  de  función  vacío  160  

propiedades  de  entradas  
Interfaz  de  mapa  280,  389  
campo,  respaldo  113,  172  
Interfaz  MutableMap  280  clases  
administración  de  archivos  11–12,  14–
de  enumeración  420–421
15  función  filter()  364,  371–374  función  
operador  de  igualdad  (==)  
sobre  17,  200,  267,  271  clase   filterIsInstance()  371  función  filterNot()  
de  datos  y  205  función   371,  374  función  filterTo()  371,  374  
equals()  y  192,  194–196,  265–266  función  equals()  sobre   palabra  clave  final  139  bloque  final  
192–194  clase  de  datos  y  205  anulando  196–197,  202 ,  267
241,  244

Tipo  flotante  35  
Establecer  interfaz  y  265 función  fold()  383–386,  389  función  
operador  igual  (=)  17   foldRight()  389  función  forall()  413  
excepciones  sobre  221,   función  forEach()  375–376,  382,  
239,  242,  245  atrapando  239–
389  bucles  for  alrededor  de  16–17,  76–77  
240
comando  println  en  75  hasta  la  cláusula  76  –77
ClassCastException  242–243  
creando  242  definiendo  242  
finalmente  bloque  240

estas  aqui  4 439
Machine Translated by Google

el  índice

barra  diagonal  (//)  13,  16  
GRAMO
nombres  completos  417  
programación  funcional  2,  355   funciones  generadas,  propiedades  y  205  
funciones.  Consulte  también  funciones   genéricos  y  tipos  genéricos
específicas  sobre  60  resumen  159–
alrededor  de  290–291,  306,  
163,  171,  173,  175  acceso  para  tipos   374  clases  y  293–296,  302,  308,  313,  315  
anulables  224–225  argumentos  y  13,  64– colecciones  y  290–293,  297–300  compilador  
65  llamadas  a  referencias  de  objetos  144   que  infiere  299–300  constructores  y  314
funciones  de  componenteN  199  concretas  
171 contravariante  315–316,  319  
covariante  308,  319  funciones  y  
conversión  40  
293,  297–298,  300  interfaces  y  293,  
creando  64   305–308,  315  invariante  316,  319
declarando  66  
con  valores  predeterminados  
Enfoque  de  Java  versus  Kotlin  319  
210  clases  de  enumeración   objetos  anulables  302  y  299,  307,  314  
y  421  extensiones   polimorfismo  y  293,  307  prefijo  con  en  
agregando  429  generado   290,  315–316  prefijo  sin  290,  308,  315  
205  genéricos  y  293,  297–298,  300   propiedades  y  297  restricción  a  tipos  
de  orden  superior  339–343,  355,  433   específicos  296  subtipos  y  308,  315  
infijo  433  herencia  y  122,  125–127,  
supertipos  y  296,  308,  315  parámetros  
144–  145  interfaz  171,  173,  175  lambdas  y  339– de  tipo  y  292  formas  de  usar  293–294  
343,  345,  347–352  función  principal  13–14,  16,   la  función  get()
21  miembro  94,  96  comportamiento  de  objeto  y  
40  de  objetos  93  sobrecarga  150,  211  anulación.  
Ver  parámetros  de  funciones  anuladas  y  64–
65,  147,  150,  210,  214,  308  argumentos  de  
paso  y  64–65  polimorfismo  y  147  prefijo  con   Interfaz  de  lista  256
final  139  prefijo  con  tipos  de  retorno  abiertos  
Interfaz  de  mapa  277
137  y  66,  147,  211,  222  sin  valores  de  retorno  
captadores  (accesorios)  111–113,  137,  163,  172  
66,  376  con  valores  de  retorno  66,  68  expresión  única  
66  Plantillas  de  cadena  llamando  48  suspendible  406   función  getValue()  (mapa)  277  GlobalScope.launch  
actualizando  4,  17–21  tipos  de  función  331,  352–354   403–406
palabra  clave  divertida  13 Herramienta  de  compilación  Gradle  398–400

Operador  mayor  que  (>)  17  
Operador  mayor  o  igual  que  (>=)  17  Función  
groupBy()  381–382

H
Prueba  HAS­A  129

función  hashCode()  194,  196–197,  202,  266–267
códigos  hash  265–267

función  hashMapOf()  282

Índice  440
Machine Translated by Google

el  índice

números  hexadecimales  35 Prueba  IS­A  129–130,  169,  193  

funciones  de  orden  superior   polimorfismo  y  147  propiedades  y  
122,  125–127,  145  subtipos  y  145  
sobre  339,  389  
colecciones  integradas   inicialización
363–394  y  364  
programación  funcional  y  355  prefijo  en   propiedades  abstractas  y  159,  162  
línea  433  lambdas  y  339–343 propiedades  de  interfaz  y  172  objetos  y  
99,  107  propiedades  y  99,  106,  108  
propiedad  206  superclases  y  136  variables  

I y  37

if  expresión  
sobre  20   bloques  inicializadores  107,  136
else  cláusula  20  
init  palabra  clave  107  
tipos  anulables  y  233  simple  
en  palabra  clave  290,  315–316  
67
Plantillas  de  cadenas  que  evalúan  arreglos  48 palabra  clave  en  línea  433
si  declaración clases  internas  425
about  19   en  operator  81  
else  cláusula  19  
instalando  IntelliJ  IDEA  IDE  4,  7  instancias.  
es  operador  y  182
Ver  variables  de  instancia  de  objetos.  ver  
excepción  de  argumento  ilegal  242,  244
propiedades
IllegalStateException  242  
instanciación
inmutabilidad  de  clases  202  de  
clases  abstractas  y  157  
tipos  de  colección  255–256,  
interfaces  y  170
263–264,  282  etiquetas  implícitas  431  declaración  de  
IntelliJ  IDEA  IDE  
importación  401,  417  operador  de  incremento  (++)  76,  430  
instalación  4,  7  
índice  (índices)  45,  58,  77,  255–258  indexOf( )  función   procesamiento  Ejecutar  comando  15
(Lista)  256  palabra  clave  infija  433 Menú  de  herramientas  23

concha  interactiva.  Ver  REPL

interfaces
acerca  de  
170  definición  
herencia
de  171  funciones  en  171,  173,  
unas  122  
175  genéricos  y  293,  305–308,  315  
clases  abstractas  y  162
implementación  de  174  herencia  y  175  
Cualquier  superclase  y  193–194,  196  evitar   creación  de  instancias  y  170  convenciones  
código  duplicado  con  122,  125  construir  jerarquía   de  nomenclatura  175  polimorfismo  y  170,  
de  clase  133–140  jerarquía  de  clase  usando  144–
181  propiedades  en  171–175  consejos  
150  diseñar  estructura  de  clase  123–130  
al  crear  175  modificadores  de  visibilidad  
funciones  y  122,  125–127,  144–145
y  419

HAS­A  prueba  129  
interfaces  y  175

estas  aqui  4 441
Machine Translated by Google

el  índice

modificador  interno  418–419 etiquetado  431  

interoperabilidad  434 parámetros  y  327,  331–332,  339–343  atajos  para  
328,  342,  345  variables  y  328,  331–333,  376  
Tipo  int  35  
palabra  clave  lateinit  108  función  de  lanzamiento  
tipos  genéricos  invariantes  316,  319  
403–406
función  de  invocación  ()  328

Prueba  IS­A  129–130,  169,  193  es  
operador  menor  que  (<)  17  
operador  181–184,  242
operador  menor  o  igual  que  (<=)  17  palabra  
Interfaz  iterable  389
clave  let  231–232,  333  vinculación  de  variables  
palabra  clave  it  231–232,  332–333,  340,  376
a  objetos.  Consulte  las  referencias  de  objetos.  Interfaz  de  

j lista  255–256,  263,  271,  371–374,  386,  389  Función  listOf()  (Lista)  

256,  263  Tipo  genérico  localmente  contravariante  316  Tipo  

Bibliotecas  Java  401 genérico  localmente  covariante  319  Variables  locales  64,  72

Lenguaje  de  programación  Java  319,  434

JavaScript  3,  434

Máquinas  virtuales  Java  (JVM)  3 Construcciones  

Biblioteca  JUnit  410–412 de  bucle  de  tipo  largo  
35  do­while  17  
@JvmOverloads  anotación  214
para  16–17,  75–77  
JVM  (Java  Virtual  Machines)  3
etiquetando  430  

k función  principal  usando  16  
while  17–18

propiedad  de  claves  (mapa)  280,  389  
METRO
pares  clave/valor  276–277,  280,  297  paquete  
función  principal  
kotlin.collections  254
acerca  de  
Bibliotecas  de  extensión  de  Kotlin  429
13  agregando  a  la  aplicación  
paquete  kotlin  254
14  bifurcación  condicional  en  16  
Lenguaje  de  programación  Kotlin  2–3 bucles  en  16  declaraciones  sin  
Biblioteca  estándar  de  Kotlin  254 parámetros  13  en  16  actualización  
21 )  276  Función  Math.random()  47  
Biblioteca  KotlinTest  412–413
Función  maxBy()  365–366  Función  
extensión  de  archivo  kt  12
max()  252,  365–366  Funciones  miembro  

L (métodos)  94,  96  Función  minBy()  365–366

etiquetas/anotaciones  (@)  411,  430–431
lambdas

sobre  325–327,  345  cierre  
y  376,  389  programación  
funcional  y  355  funciones  y  339–343,  345,  
347–352  invocación  328–330

442  Índice
Machine Translated by Google

el  índice

función  min()  252,  365–366  

modificadores,  visibilidad  418–419  
O
mutabilidad  de  arreglos  253  de  tipos   declaraciones  de  objeto  98,  426–427,  429  
de  colección  255,  282
expresiones  de  objeto  428  palabra  clave  de  

objeto  426  referencias  de  objetos  matrices  y  45,  
Interfaz  MutableList  255,  257–259,  263,  291–293,  300  Función  
49–51,  69–71  asignación  de  funciones  33–34,  
mutableListOf()  (MutableList)  257,  291,  300
38,  98  llamadas  a  144  eliminación  de  
Interfaz  mutableMap  255,  278–280,  297  Función   variables  220–221  eliminación  usando  nulo  
mutableMapOf()  (mapa)  278 221  objetos  clases  abstractas  y  157  

Interfaz  MutableSet  255,  264,  268–269,  298–299  Función   constructores  y  99–102  creación  95,  98–
100  creación  a  partir  de  clases  de  datos  
mutableSetOf()  (MutableSet)  268  mutadores  (establecedores)  
196  definición  de  tipos  92  función  igual  y  192  
111–113,  137,  163,  172 funciones  de  93  genéricos  y  299,  307,  314  
inicialización  99,  107  propiedades  de.  Ver  

norte propiedades  abriendo  REPL  23  palabra  
clave  abierta  134,  137,  139,  159  u  operador  
argumentos  con  nombre  208   (||)  81,  182
convenciones  de  nomenclatura  para  interfaces  

175  variables  de  nomenclatura  16,  32,  38  código  

nativo  3,  434
clases  anidadas  424–425

función  nextInt()  (aleatoria)  48  operador  

no  igual  (!=)  82,  224  tipo  Nothing  245  

operador  de  aserción  no  nulo  (!!)  234  

operador  no  (!)  82,  182  tipos  anulables   clases  externas  424–425

funciones  de  acceso  224–225  propiedades   out  palabra  clave  290,  308,  315  

de  acceso  224–  225  matrices  de  223,  253   funciones  de  sobrecarga  150,  211
código  de  ejecución  condicional  231   funciones  anuladas
genéricos  y  302  llamadas  seguras  y   clases  de  datos  y  197  
225–228  formas  de  usar  222–223   interfaces  y  173  palabra  
NullPointerException  221,  234,  242 clave  abierta  y  134,  139  funciones  
sobrecargadas  versus  211  reglas  para  
138,  267  subclases  y  122,  126,  150  
formas  de  usar  138  propiedades  anuladas

valor  nulo
alrededor  de  78 interfaces  y  173
comprobando  81   palabra  clave  abierta  y  134,  139  
tipos  anulables  y  221–224  llamadas   subclases  y  122,  126  palabras  clave  
seguras  y  225–228 val  y  var  137,  150  formas  de  usar  136–
137  palabra  clave  anulada  136–138

estas  aqui  4 443
Machine Translated by Google

el  índice

comando  de  impresión  
PAG
18  comando  println  
aproximadamente  
paquetes  254,  259,  416–418  
13  en  bucle  for  75  
ejecución  paralela  402–404
imprimir  frente  a  
parámetros   18  función  printStackTrace()  242  
alrededor  de  
modificador  privado  418–419  
64–65  con  valores  predeterminados  
210,  214  constructores  vacíos  y   proyectos  agregando  clases  a  133,  
109  funciones  y  64–65,  147,  150,  210,  214,  308   143  agregando  archivos  a  11–
lambdas  y  327,  331–332,  339–343  variables  locales  y   12  configurando  10  creando  4,  
72  tipos  anulables  222  orden  de  argumentos  y  65   8–12,  62  especificando  tipos  de  
prefijo  con  val/var  105,  120,  206  propiedades  como   9  carpeta  src  y  11–12
105  separando  múltiples  65  constructores  de  
superclase  y  135  tipo  292  tipos  de  variables  que  
coinciden  65  paréntesis  ()  argumentos  y  13 propiedades  
sobre  40,  93,  102  
resumen  159–163,  173  
acceso  96  asignación  de  
valores  predeterminados  a  206  
constructores  que  definen  100,  102,  205  
valores  de  ocultación  de  datos  114  objetos  
Expresiones  booleanas  y  82   de  datos  y  197  declaración  112  definición  
parámetros  lambda  y  343   en  el  cuerpo  principal  de  la  clase  106  
constructores  de  superclase  y  173   clases  de  enumeración  y  421  extensiones  
agregando  429  inicialización  flexible  106  
pasar  valores  para  argumentos  sin  valores  
funciones  generadas  y  205  genéricos  y  
predeterminados  208  en  orden  de  declaración   297  herencia  y  122,  125–127,  145  
207
inicialización  99,  106,  108,  206  interfaz  
plataformas  
171–175  tipos  anulables  222,  224–225  
que  especifican  para   anulación.  Ver  propiedades  anuladas  
proyectos  9  compatibles  con   como  parámetros  105  prefijar  con  final  
la  función  Kotlin  3  plus()  (Array)   139  prefijar  con  abierto  137  Plantillas  de  

253  polimorfismo  sobre  147,   cadena  que  hacen  referencia  48  validar  
150,  161  funciones   valores  111–113  modificador  protegido  419  
abstractas  y  161  propiedades   modificador  público  418–419  función  
putAll()  (mapa)  278  función  put()  (mapa)  
abstractas  y  161
278
cualquier  superclase  y  193  
genéricos  y  293,  307  clases  
independientes  y  169  interfaces  y  
170,  181  constructores  primarios  
sobre  99,  214  clases  de  datos  y  205–
209  modificador  privado  419  
superclases  y  135–136,  173

444  Índice
Machine Translated by Google

el  índice

q funciones  con  66,  68  
funciones  sin  66,  376  propiedades  
de  interfaz  y  172  lambdas  y  331  valor  
signo  de  interrogación  (?)  222–223 nulo  78,  81  función  invertida()  259  

R función  inversa()

Random.nextInt()  función  48  generación  
Clase  de  matriz  252
de  números  aleatorios  47–48  rango  de  números  
MutableList  subtipo  259
en  bucle  en  orden  inverso  77  bucle  a  través  de   Juego  de  piedra,  papel  o  tijera  
76  saltar  números  77  operador  de  rango   elección  del  juego  63–71  
(..)  76–77  lectura  de  la  entrada  del  usuario   diseño  de  alto  nivel  61–62  
78  función  readLine()  78,  81  reduce()   resultado  87–88  reglas  de  60  
función  389  reduceRight()  función  389  operador   elección  del  usuario  75–78,  81–

de  igualdad  referencial  (===)  200,  265–267   84  función  fila()  413

función  removeAll()  interfaz  MutableList  259  
reglas  
interfaz  MutableSet  268
para  clases  de  datos  217  
para  excepciones  244  
para  funciones  anuladas  138,  267  función  

runBlocking()  405
Ejecutar  comando  15

función  removeAt()  (MutableList)  258  función   S
remove()
Interfaz  MutableList  258 operador  de  llamada  segura  (?.)  225,  231–232
Interfaz  MutableMap  279 llamadas  seguras

Interfaz  MutableSet  268 sobre  225  
REPL  (shell  interactivo)   asignando  valores  con  228  
alrededor  de  7  abriendo   encadenando  juntos  226  evaluando  
23  código  de  prueba  en   cadenas  226–227  moldes  explícitos  
4,  23–24  función  de  retención  () seguros  243  clases  selladas  422–423

Interfaz  MutableList  259
constructores  secundarios  209,  214  función  
Interfaz  MutableSet  268
declaración  de  devolución  430–431 set()  (MutableList)  258

Establecer  interfaz  255,  264–269,  271,  282,  389  función  
funciones  de  
tipo  de  retorno  y  66,  147,  211  tipos   setOf()  (Set)  264  setters  (mutadores)  111–113,  137,  

genéricos  y  315  funciones  de  orden   163,  172  cortocircuito  81
superior  y  366  lambdas  y  347–348  tipos  
anulables  222
Tipo  corto  35  

Unidad  66,  333 expresión  shouldBe  412  función  
valores  devueltos shuffled()  (MutableList)  259
expresiones  y  20,  183,  245

estas  aqui  4 445
Machine Translated by Google

el  índice

función  shuffle()  (MutableList)  259  funciones   polimorfismo  y  150,  161  clases  
selladas  y  422–423
de  expresión  única  67
función  sumByDouble()  367  función  
Propiedad  de  tamaño  de  
sumBy()  367  función  sum()  (Array)  
patrón  Singleton  429
Clase  de  matriz  45,  252 252  superclases  sobre  122  abstractas  
Interfaz  de  lista  256,  263 157,  162  que  declaran  134,  139  
Interfaz  mutableSet  269  Función   funciones  y  122,  125–126,  138–
sleep()  406  Smart  Casts  184,  243 139,  144–145  herencia.  ver  
herencia

función  sortBy()  (MutableList)  326  función  

sorted()  (MutableList)  259  función  sort() polimorfismo  y  161  constructores  
primarios  135–136,  173  propiedades  y  122,  

Clase  de  matriz  252 125–126,  136–137,  139,  145

MutableList  subtipo  259  operador   supertipos  

de  propagación  (*)  432 genéricos  296,  308,  315  
herencia  y  145–147  polimorfismo  
carpeta  src
y  161  funciones  suspendibles  406
agregar  archivos  al  proyecto  11–12  
archivos  de  código  fuente  en  11

T
declaraciones

if  17–19,  182  
import  401,  417  
clases  de  
función  principal  usando  16  
when  182–183 plantillas  como  91–92,  95,  175
Cuerda  47–48
estado  (objetos)  40,  124.  Consulte  también  propiedades  
@Anotación  de  prueba  411
que  almacenan  valores  en  matrices  45  Plantillas  de  
prueba­introducción  xxiii
cadena  47–48  tipo  de  cadena  13,  36
pruebas  y  pruebas
Prueba  HAS­A  129
subclases
Prueba  IS­A  129–130,  169,  193
sobre  122   Biblioteca  JUnit  410–412
añadiendo  constructores  a  135   Biblioteca  KotlinTest  412–413
definiendo  135  funciones  y  122,   Ejecutar  comando  y  15
125–126,  138–139,  144–147  herencia.  ver  herencia hilos  404–406

bloques  inicializadores  en  136   lanzar  excepciones  234,  239,  244  lanzar  

polimorfismo  y  147,  161  propiedades   palabra  clave  244–245  función  toByte()  40  
y  122,  125–126,  137,  145 función  toDouble()  40  función  toFloat()  40  

función  toInt()  40,  47  función  toList()

Clase  de  matriz  271
Interfaz  de  mapa  280

446  Índice
Machine Translated by Google

el  índice

Interfaz  MutableList  259
Interfaz  MutableMap  280
Establecer  interfaz  269
tu
Tipo  de  retorno  de  unidad  66,  333  
función  toLong()  40–41  función  
prueba  de  unidad  410–411  hasta  
toLowerCase()  88  función  toMap()  
la  cláusula  (para)  76–77  funciones  
(MutableMap)  280  función  toMutableList()
de  actualización  4,  17–21  entrada  de  

Clase  de  matriz  271 usuario  78,  81

Interfaz  MutableList  259,  271
Interfaz  MutableMap  280  Función  
toMutableMap()  (MutableMap)  280  Función  toMutableSet()  
V
validación  
(Array)  271
de  valores  de  propiedad  111–113  
Menú  Herramientas  (IntelliJ  IDEA)  23   entrada  del  usuario  81  palabra  
Función  toSet()  sobre  282 clave  val  sobre  16,  282  asignación  de  
lambdas  a  variables  328  
Clase  de  matriz  271 declaración  de  matrices  usando  51  definición  
Interfaz  de  mapa  280 de  propiedades  y  102  captadores  y  definidores  
Interfaz  MutableSet  269  Función  
114  anulación  de  propiedades  y  137,  150  
toShort()  40  Función  toString()  194,   variables  de  parámetros  y  72  prefijo  de  
parámetros  con  105,  120,  206  var  contra  16,  
196–197,  202  Función  toTypedArray()
34,  102

Interfaz  de  lista  271
Establecer  interfaz  271
valores
función  toUpperCase()  88,  106  bloque  de  
asignar  32–34,  37–39  asignar  
prueba  (try/catch)  138,  240–241,  244–245  complemento  a   a  llamadas  seguras  228  convertir  
dos  42  palabra  clave  typealias  353  parámetros  de  tipo  292 40–42  propiedad  de  ocultar  datos  
114  duplicar  255,  263,  265,  269  
clases  de  enumeración  420  inferir  tipo  
de  matriz  de  49  inicializar  para  

tipos   variables  37  estado  de  objeto  y  95,  
de  colecciones  255–256,  263–264,  282  conversión   124  devolver  20,  66  reutilización  de  
de  valores  de  40–42  función  331,  352–354   16,  32,  34,  50–51  almacenamiento  en  
genérico.  Ver  genéricos  y  tipos  genéricos  que   arreglos  45  propiedad  de  validación  
infieren  para  matrices  49  que  aceptan  valores   111–113  propiedad  de  valores  (mapa)  
NULL  223–228,  231,  253,  302  devuelven  66,  147,   280,  389  palabra  clave  vararg  432  
211,  222,  315  subtipos.  Ver  subtipos  supertipos.   variables
Ver  supertipos  variable  32–38

sobre  32,  34  
asignación  de  valores  32–34,  37–39

estas  aqui  4 447
Machine Translated by Google

el  índice

Pruebas  booleanas  en   control  de  versiones,  IntelliJ  IDEA  y  7  
17  comparación  de  opciones   modificadores  de  visibilidad  418–419
para  183  conversión  de  valores  
40–41  creación  32  declaración  
32–37,  98,  331  inicialización   W
37  instancia  102  lambdas  y  
cuando  expresión  183
328,  331–333,  376  local  64,  72  
declaración  cuando  182–183
tipo  de  parámetro  coincidente  65  
nomenclatura  16,  32,  38  referencias   while  bucles  
a  objetos  y  Ver  referencias  de  objetos   sobre  16–17,  76,  81  
prefijar  con  $  48  reutilización  de  16,   pruebas  condicionales  17–
32,  34,  50–51  tipos  de  32–38  var  palabra  clave   18  es  operador  y  182  
sobre  16,  282  asignar  lambdas  a  variables  328   espacio  en  blanco  16  función  
declarar  matrices  usando  50  definir  propiedades  
withIndex()  (Array)  77  escribir  getters/
y  102  getters  y  setters  114  lateinit  palabra  clave  y  
108  anulando  propiedades  y  137,  150  parámetros  de   setters  personalizados  112
prefijo  con  105,  120,  206  fundición  inteligente  y  
184  propiedades  de  actualización  y  96  val  versus  
16,  34,  102

448  Índice

También podría gustarte