Las clases anidadas (nested) e internas (inner) en Kotlin se utilizan para la agrupación lógica y la encapsulación de la funcionalidad dentro de la clase externa.
La idea de las clases anidadas proviene de Java como una forma de estructurar el código y aislar componentes auxiliares dentro de la clase principal. En Kotlin, la sintaxis y el enfoque se heredaron de Java, pero con diferencias importantes.
El principal objetivo es separar adecuadamente las clases auxiliares donde no deberían existir fuera del contexto de la clase externa, pero al mismo tiempo se requiere un diferente nivel de acceso a los miembros de la clase externa. En Java, por defecto, una clase anidada es una clase interna, en Kotlin por defecto es anidada (estática).
En Kotlin, por defecto, declarar una clase dentro de otra crea una clase estática (nested), es decir, esta clase no tiene acceso a los miembros de la clase externa. Para obtener acceso, se utiliza la palabra clave inner.
Ejemplo de código:
class Outer { private val secret = "fuera" class Nested { fun call() = "anidado: sin acceso a Outer.secret" } inner class Inner { fun call() = "interno: puede acceder a $secret" } }
Características clave:
class Nested) por defecto no tiene referencia a la instancia de la clase externa;inner class Inner) tiene referencia y puede acceder a los miembros de la clase externa, incluso a los privados;¿Puede una clase anidada (nested) acceder a una propiedad privada de la clase externa?
No, la clase anidada (por defecto) en Kotlin es estática y no contiene referencia a la clase externa, por lo que no tiene acceso a sus propiedades y métodos.
¿Cuál es la diferencia entre las clases inner en Kotlin y Java?
En Java, la clase anidada por defecto no es estática y tiene referencia a la externa. En Kotlin, al contrario; la clase anidada es estática, solo la clase inner obtiene la referencia a la instancia externa.
¿Se puede declarar una clase interna en un objeto (object)?
No, una clase interna (inner) no puede ser declarada dentro de un objeto, porque un objeto no puede ser instanciado.
Un desarrollador declara una clase inner que no utiliza propiedades de la clase externa:
class Container { inner class Helper { fun help() = "ayuda" } }
Pros:
Fácil de obtener desde el objeto externo.
Contras:
Uso de una clase interna para implementar acceso al estado privado de la clase externa:
class Auth { private var token: String = "" inner class TokenManager { fun updateToken(new: String) { token = new } } }
Pros:
Contras: