ProgrammationDéveloppeur Backend

Comment fonctionne le mécanisme des modificateurs de visibilité (internal/private/protected/public) pour les fonctions et propriétés de niveau supérieur en Kotlin ? Quelles sont les différences avec Java et quelles subtilités faut-il prendre en compte ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En Kotlin, les modificateurs de visibilité permettent de contrôler l'accès aux déclarations : classes, propriétés, fonctions et entités de niveau supérieur (au niveau du fichier). Contrairement à Java, où les modificateurs n'agissent qu'au niveau de la classe, en Kotlin ils fonctionnent également pour les déclarations de niveau supérieur, ce qui est important pour structurer de grands projets et des API de bibliothèque.

Historique de la question

En Java, il n'existe pas de modificateurs de visibilité pour les fonctions ou propriétés en dehors des classes — tout est contenu dans une classe public (ou package-private). En Kotlin, on structure souvent le projet différemment, où une fonction ou une propriété peut ne pas être dans une classe mais directement dans un fichier.

Problème

Les développeurs Java s'attendent souvent à ce que public par défaut fonctionne de la même manière qu'en Java, mais en Kotlin une fonction de niveau supérieur (ou une propriété) est visible dans tous les modules, sauf mention contraire. Une définition incorrecte de la visibilité peut entraîner un encombrement du public API, une accessibilité inattendue des utilitaires internes, ou l'inaccessibilité de fonctions publiques nécessaires.

Solution

En Kotlin, les modificateurs suivants sont disponibles :

  • public : la déclaration est visible partout (c'est le modificateur par défaut pour le niveau supérieur).
  • internal : la déclaration est visible dans tous les fichiers du même module (un module Gradle, un artefact compilé, un JAR).
  • private : visible uniquement dans le même fichier/classe où elle est déclarée. Pour le niveau supérieur - uniquement à l'intérieur du fichier.
  • protected : non applicable aux déclarations de niveau supérieur, uniquement pour les classes/interfaces et leurs sous-classes.

Exemple :

// fichier : Foo.kt private fun utilityFun() {} internal val bar: Int = 10 public val baz: Int = 20 // public n'est pas obligatoire fun printValue() { println(bar) }

Caractéristiques clés :

  • internal limite la visibilité au module (JAR/artefact), et non au package.
  • protected ne peut pas être utilisé pour des fonctions ou propriétés de niveau supérieur.
  • private au niveau supérieur limite la déclaration aux frontières du fichier actuel.

Questions pièges.

Peut-on utiliser protected pour une fonction de niveau supérieur ?

Non, protected est pertinent uniquement pour les membres de classe/interface, les éléments de niveau supérieur ne le prennent pas en charge.

Si l'on déclare une fonction de niveau supérieur avec internal, sera-t-elle visible dans d'autres modules ?

Non. Elle sera visible uniquement dans le cadre du JAR/module Gradle actuel.

Quelle est la différence entre une classe private et une fonction de niveau supérieur private ?

  • classe private : visible uniquement dans le fichier actuel, ne peut pas être utilisée en dehors du fichier.
  • fonction ou propriété de niveau supérieur private : similaire, visible uniquement à l'intérieur du fichier.

Exemple :

// fichier : Utils.kt private fun helper() { /* ... */ } // visible uniquement dans ce fichier internal fun useful() { /* ... */ } // visible dans tout le module

Erreurs courantes et anti-patterns

  • Utiliser public par défaut pour toutes les déclarations entraînant un "encombrement" de l'autocomplétion et de l'API.
  • Utiliser internal pour une bibliothèque destinée à des clients externes, cachant les API publiques nécessaires.
  • Confusion avec protected et tentatives d'application de ceux-ci au niveau supérieur.

Exemple concret

Cas négatif

Les utilitaires de test sont déclarés public et se retrouvent dans l'artefact, gênant le client de la bibliothèque — tout ce qui ne concerne pas l'API publique devient visible.

Avantages :

  • Intégration rapide.

Inconvénients :

  • La taille de l'API publique augmente, des méthodes "aléatoires" deviennent accessibles.

Cas positif

Les fonctions internes sont déclarées private, les utilitaires avec visibilité internal pour l'utilisation générale à l'intérieur du module, seules des interfaces soigneusement pensées ont un accès public.

Avantages :

  • Structure API claire et propre.
  • Minimisation des dépendances accidentelles.

Inconvénients :

  • Nécessité de réfléchir à la structure du projet.