ProgramaciónDesarrollador Backend

¿Cómo funciona el mecanismo de modificadores de visibilidad (internal/private/protected/public) para funciones y propiedades de nivel superior en Kotlin? ¿Cuáles son las diferencias con Java y qué matices deben tenerse en cuenta?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Kotlin, los modificadores de visibilidad permiten controlar el acceso a las declaraciones: clases, propiedades, funciones y entidades de nivel superior (a nivel de archivo). A diferencia de Java, donde los modificadores solo actúan a nivel de clase, en Kotlin también funcionan para declaraciones de nivel superior, lo cual es importante para estructurar grandes proyectos y API de bibliotecas.

Historia del asunto

En Java no existen modificadores de visibilidad para funciones o propiedades fuera de una clase; todo está dentro de una clase pública (o package-private). En Kotlin, es común estructurar el proyecto de otra manera, a menudo una función o propiedad se encuentra no dentro de una clase, sino directamente en el archivo.

Problema

A menudo, los desarrolladores de Java esperan que public por defecto funcione igual que en Java, pero en Kotlin, una función de nivel superior (o propiedad) es visible en todos los módulos, a menos que se indique lo contrario. Una definición incorrecta de la visibilidad puede llevar a la contaminación léxica de la API pública, a la disponibilidad inesperada de utilidades internas, o a la inaccessibilidad de funciones públicas necesarias.

Solución

En Kotlin, están disponibles los siguientes modificadores:

  • public: la declaración es visible en todas partes (es el modificador por defecto para nivel superior).
  • internal: la declaración es visible en todos los archivos del mismo módulo (un módulo de gradle, un artefacto compilado, un jar).
  • private: visible solo en el mismo archivo/clase donde se declara. Para nivel superior, solo dentro del archivo.
  • protected: no aplicable para declaraciones de nivel superior, solo para clases/interfaces y sus subclases.

Ejemplo:

// archivo: Foo.kt private fun utilityFun() {} internal val bar: Int = 10 public val baz: Int = 20 // public no es necesario fun printValue() { println(bar) }

Características clave:

  • internal limita la visibilidad al módulo (jar/artefacto), no al paquete.
  • protected no puede usarse para funciones o propiedades de nivel superior.
  • private en nivel superior limita la declaración a los límites del archivo actual.

Preguntas capciosas.

¿Se puede usar protected para una función de nivel superior?

No, protected es relevante solo para miembros de clase/interfaz; los elementos de nivel superior no lo soportan.

Si declaro una función de nivel superior como internal, ¿será visible dentro de otros módulos?

No. Será visible solo dentro del jar/módulo de Gradle actual.

¿Cuál es la diferencia entre una clase privada y una función de nivel superior privada?

  • clase privada: visible solo dentro del archivo actual, no puede ser usada fuera del archivo.
  • función de nivel superior privada o propiedad: similarmente, visible solo dentro del archivo.

Ejemplo:

// archivo: Utils.kt private fun helper() { /* ... */ } // visible solo en este archivo internal fun useful() { /* ... */ } // visible en todo el módulo

Errores típicos y anti-patrones

  • Usar public por defecto para todas las declaraciones lleva a la "contaminación" de la autocompletación y la API.
  • Usar internal para una biblioteca destinada a clientes externos oculta la necesaria API pública.
  • Confusión con protected y los intentos de aplicarlos a nivel superior.

Ejemplo de la vida real

Caso negativo

Las utilidades de prueba se declaran públicas y así llegan al artefacto, impidiendo al cliente de la biblioteca — se hace visible todo lo que no está relacionado con la API pública.

Pros:

  • Rápida integración.

Contras:

  • Crece el tamaño de la API pública, aparecen métodos "accidentales" accesibles.

Caso positivo

Las funciones internas se declaran privadas, utilidades con visibilidad internal para uso común dentro del módulo, solo las interfaces cuidadosamente pensadas tienen acceso público.

Pros:

  • Estructura de API clara y limpia.
  • Se minimizan las dependencias accidentales.

Contras:

  • Necesidad de planear la estructura del proyecto.