ProgramaciónDesarrollador Backend

Explique las sutilezas del uso del operador 'object' en Kotlin: ¿qué son los objetos singleton, las expresiones de objeto, las declaraciones de objeto y los objetos compañeros? Proporcione ejemplos de uso y posibles errores.

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Kotlin amplía el concepto clásico de singleton a través de la palabra clave object. Con ello se implementan los siguientes patrones:

  • Declaración de objeto (object declaration) — crea una única instancia para toda la aplicación (object Logger { ... }).
  • Expresión de objeto (object expression) — crea un objeto anónimo directamente en el lugar de uso, por ejemplo, para implementar interfaces o controladores de eventos.
  • Objetos compañeros (companion object) — permiten declarar miembros estáticos en una clase.

Ejemplo de objeto singleton:

object DatabaseManager { fun connect() { /*...*/ } } DatabaseManager.connect()

Expresión de objeto:

val listener = object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { /*...*/ } }

Objeto compañero:

class User { companion object Factory { fun create(name: String) = User() } } val user = User.create("Ivan")

Matices:

  • Los objetos compañeros se ven como campos estáticos a nivel de bytecode.
  • Los objetos compañeros pueden implementar interfaces.
  • La expresión de objeto no es un singleton, se crea cada vez que se accede a ella.
  • La declaración de objeto se inicializa de manera perezosa, en el primer acceso.

Pregunta capciosa.

¿Cuál es la diferencia entre companion object y object declaration? ¿Son accesibles sus miembros como estáticos?

Respuesta:

  • object declaration — singleton global, miembro de clase, interfaz o nivel externo.
  • companion object — un tipo especial de object declaration dentro de una clase, cuyos miembros se pueden llamar como si fueran estáticos (a través del nombre de la clase). Sin embargo, a diferencia de Java, son en realidad campos del objeto singleton.

Ejemplo de la diferencia:

class A { companion object { fun foo() {} } object NestedObj { fun bar() {} } } A.foo() // OK A.NestedObj.bar() // OK, pero no es un método estático

Ejemplos de errores reales por desconocer las sutilezas del tema.


Historia

Un desarrollador definió un estado mutable dentro de una declaración de objeto y comenzó a usarlo desde diferentes hilos sin sincronización, sin darse cuenta de que los objetos singleton se comparten en toda la aplicación y pueden causar condiciones de carrera.


Historia

Al declarar un objeto en lugar de un companion object dentro de una clase, se requería el uso de métodos estáticos, pero tuvieron que ser llamados a través de la instancia, lo que deterioró la legibilidad y provocó errores al migrar desde Java.


Historia

En el código de UI, el programador creaba un nuevo objeto cada vez a través de la expresión de objeto para el controlador de eventos. Erróneamente pensaba que era un singleton y que el estado se mantendría; como resultado, surgieron fugas de memoria debido al manejo incorrecto del ciclo de vida.