Langage Java du point de vue de Kotlin et C #

introduction

D'après le titre "Java language from the perspective of Kotlin and C #", je pense que certaines personnes qui connaissent Kotlin et C # s'attendraient à quelque chose comme un rapport des résultats de l'expérience de Java, mais cela a été fait. Pas une phrase. Il a été écrit par une personne qui avait joué avec Java et C #, inspiré par le livre "Kotlin In Actin" (et a également lu un livre C # pour des raisons commerciales). Ce livre était un livre très intéressant et m'a donné une compréhension plus profonde du langage Java ainsi que des connaissances de Kotlin.

Je pense que cet article sera un matériau de réflexion sur les langages de programmation, et je vais le laisser.

Faiblesses de Java

Pourquoi étudier Kotlin et C # vous donne un aperçu de Java? C'est parce que les deux langages sont conçus avec Java comme référence et pour le rendre plus facile à utiliser. Par conséquent, si vous regardez les parties complétées par les deux langages, vous pouvez voir ce que Java a reconnu comme une faiblesse.

Alors, quelles sont les faiblesses de Java? Il y en a beaucoup, mais ici je vais me concentrer sur trois choses: (1) le manque de fonction de définition du type de valeur, (2) l'héritage des fonctions virtuelles et (3) le manque de sécurité NULL. Les trois sont étroitement liés les uns aux autres.

Manque de fonction de définition du type de valeur

Un type valeur est un type représenté par sa valeur réelle et une variable de type valeur contient directement sa valeur. Les types de valeur n'existent que dans les types de données de base tels que booléen, int et double en Java. Le contraire est un type de référence, et les variables de ce type ne contiennent pas de valeur directe, mais ont cette référence comme valeur. En Java, tous les types à l'exception du type de données de base sont des types de référence.

En Java, tous les types autres que les types de données de base, à la fois les types pré-préparés et les types définis par l'utilisateur, sont des types de référence. Autrement dit, l'utilisateur ne peut pas définir le type de valeur.

En revanche, C #, qui a été créé en référence à Java, a fourni une fonction de langage appelée structure permettant à l'utilisateur de définir un type de valeur depuis la version initiale.

Le type de valeur permet de confirmer facilement que les deux valeurs sont identiques (appelée équivalence) et d'en faire une copie. Par exemple, dans les types de base Java, l'opérateur d'égalité (==) peut être utilisé pour confirmer que deux valeurs sont égales et l'opérateur d'affectation (=) peut être utilisé pour faire une copie de la valeur.

Dans le cas d'un type référence, l'opérateur d'équivalence vérifie que les valeurs de référence sont égales, c'est-à-dire qu'elles renvoient à la même chose (dans ce cas, elles renvoient à la même chose), et l'opérateur d'affectation vérifie que la valeur de référence est égale. Faites une copie, pas une copie de la valeur réelle.

Pour vérifier l'équivalence avec les types de référence Java, vous devez remplacer la méthode equals (et hashCode en même temps), et si vous avez besoin d'une copie de l'objet, vous devez remplacer la méthode clone ou fournir un constructeur de copie. Il y a.

La faiblesse du manque de types de valeur est que les applications ont de nombreuses opportunités d'utiliser ces deux fonctionnalités. Pour cette raison, lors de la création d'une application en Java, une grande quantité de plaques chauffantes (code standard requis par les spécifications du langage) peut être intégrée dans le code source (en fait, Java Le grand nombre de plaques chauffantes incorporées dans le code source est en grande partie dû au manque de fonctionnalités de langage appelées propriétés en Kotlin et C #, mais c'est compliqué ici, donc je l'omettrai ici).

Alors pourquoi Java a-t-il utilisé tous les types à l'exception du type de données de base comme types de référence? La raison est de demander à l'auteur de Java James Gothlin, mais je peux l'imaginer.

Java est un langage très vanté en tant que langage orienté objet. Au moment de la naissance de Java, les trois fonctions suivantes devraient être incluses dans les langages orientés objet.

Parmi ceux-ci, Java réalise le polymorphisme en héritant et en remplaçant des fonctions virtuelles, qui est une fonction qui ne peut être réalisée que par type de référence en Java. Pour cette raison, Java, qui est orienté vers les langages orientés objet, peut avoir essayé d'éliminer autant que possible les types valeur. On peut plutôt dire que l'utilisation du type de données de base comme type de valeur était un choix inévitable compte tenu des performances et d'autres facteurs.

De plus, Java a la fonctionnalité que «les opérateurs ne peuvent pas être surchargés», ce qui semble être lié à la fonctionnalité selon laquelle les types de valeur définis par l'utilisateur ne peuvent pas être créés.

Concernant la définition multiple des opérateurs, l'auteur de Java James Gothlin dit que:

Peut-être 20 à 30% considèrent la surcharge des opérateurs comme la racine de tout mal. Ceci est marqué d'une grande croix parce que quelqu'un quelque part a utilisé une surcharge d'opérateurs, par exemple en assignant un "+" pour insérer une liste, ce qui a énormément gâché ma vie. Ça à du être. La plupart des problèmes sont qu'il y a au plus une demi-douzaine d'opérateurs qui peuvent être surchargés de manière raisonnable, mais il y a des milliers ou des millions d'opérateurs que vous voudrez définir. Je dois choisir, mais le choix lui-même est contraire à mon intuition.

Autrement dit, James Gothlin déteste la surcharge des opérateurs.

Cependant, si Java avait adopté des types de valeur définis par l'utilisateur comme fonctionnalités de langage, il n'aurait pas été possible de choisir de ne pas adopter la surcharge d'opérateurs. Au moins, cela aurait dû être perceptible comme un défaut de langue.

Par exemple, l'un des types de valeur typiques est le type de nombre complexe. Si cela pouvait être défini, ce serait une faille linguistique que les opérateurs plus et moins pour ce type ne pourraient pas être définis. Mais s'il ne peut être défini que comme un type de référence, même si l'opérateur ne peut pas être surchargé, il ne sera pas perceptible comme un défaut linguistique.

En supprimant la fonction pour définir le type valeur, il est possible d'omettre la fonction de surcharge de l'opérateur que je déteste. Je pense que c'était très pratique pour Java.

Héritage de fonction virtuelle

Comme mentionné précédemment, Java est un langage largement présenté comme un langage orienté objet, et le langage est conçu pour prendre en charge activement le polymorphisme en héritant et en remplaçant les fonctions virtuelles. Autrement dit, si rien n'est spécifié, la classe devient une classe pouvant être héritée et la fonction définie dans la classe devient une fonction virtuelle remplaçable.

Alors, quel est le problème avec cette spécification de langage?

En fait, il y a un gros problème. Les fonctions virtuelles provoquent un problème maintenant connu appelé la "classe de base fragile". Pour cette raison, il est désormais recommandé de ne pas l'utiliser.

Le "problème de classe de base vulnérable" est le problème que lorsque le code de la classe de base est modifié, le changement n'est plus ce que la sous-classe attend, provoquant un comportement malveillant dans la sous-classe. (Pour plus de détails, veuillez vous référer à https://en.wikipedia.org/wiki/Fragile_base_class etc.)

Pour éviter ce problème, le chef-d'œuvre Java bien connu, Effective Java, recommande une méthode de "conception et de documentation pour l'héritage, ou interdisant l'héritage". En d'autres termes, lors de l'utilisation du polymorphisme par héritage, il est recommandé de publier le contenu d'implémentation de la classe de base en tant que document (sinon, l'héritage n'est pas utilisé en premier lieu). Cela signifie abandonner la «dissimulation d'informations», qui est considérée comme un élément important de l'orientation des objets.

Manque de sécurité NULL

Il y a un défaut linguistique qui se démarque car tous les types autres que le type de base sont des types de référence. C'est le manque de sécurité NULL.

Un type de référence Java est un type de référence de valeur, mais peut avoir une valeur sans référence (NUL). À ce stade, en partant du principe qu'une valeur normale est référencée, une exception appelée NullPointerException (appelée nullpo) se produit lorsqu'un membre (méthode ou champ) de ce type est référencé. Pour faire simple, la sécurité NULL est un mécanisme qui ne génère pas cette NullPointerException.

Le fait que Java ne soit pas sûr NULL est un défaut linguistique, d'autre part, parce que Java est un langage de type sécurisé (par exemple, C n'est pas un langage de type sûr, et les références NULL sont également un problème. Pas fini).

La sécurité des types est un mécanisme pour empêcher les opérations non autorisées sur les types en détectant les erreurs de compilation. Par exemple, si le type d'une variable est de type String, une tentative d'effectuer une opération sur cette variable autre que celle autorisée pour le type String entraînera une erreur de compilation en Java. Les références NULL sont la seule exception à la sécurité de type de Java.

Le problème avec le manque de sécurité NULL en Java est que Java est un langage très répandu et que de nombreux programmeurs ont souffert de ce problème, mais tous les types de données de base sont des types de référence. Pousse également cela.

Pour ajouter quelques mots à l'honneur de Java, je ne pense pas qu'il y ait eu un mot NULL safe quand Java est né. Au moins, ce n'était pas une fonctionnalité connue. Par conséquent, on peut dire que Java n'est pas sûr NULL.

Mesures contre les faiblesses

Alors, comment Java tente-t-il de surmonter ces faiblesses? Voyons également comment Kotlin et C # prennent des mesures contre cela.

Contre-mesures contre l'absence de fonction de définition du type de valeur

Pour Java

En Java, tous les types définis par l'utilisateur sont des types référence, il n'est donc pas possible de définir strictement un type valeur, mais il est possible de définir un type qui se comporte comme une valeur. En d'autres termes, il s'agit d'un type qui permet une comparaison de valeur égale avec la méthode equals et facilite la réalisation d'une copie. Cependant, dans ce cas, de nombreuses plaques de chaudière doivent être ajoutées à la source, comme décrit ci-dessus.

Pour résoudre ce problème, la première méthode qui est sortie a été la génération automatique de plaques chauffantes par IDE. Cependant, bien que cela réduise le fardeau de la création de la source, cela ne réduit pas le fardeau de l'examiner ultérieurement.

C'est là que la bibliothèque appelée Lombok est sortie. Si vous installez cette bibliothèque, vous pouvez simplement ajouter une annotation de données à la classe et elle générera automatiquement une plaque chauffante telle que égale à un endroit qui n'est pas visible depuis le code source.

De plus, Java 14 a introduit un nouveau mécanisme appelé Recod, qui a encore complété la faiblesse de Java de ne pas avoir de type valeur.

Pour Kotlin

Kotlin, comme Java, est un langage conçu pour s'exécuter sur des JVM. Par conséquent, tous les types autres que le type de données de base ont la propriété d'être des types référence et le type valeur ne peut pas être défini. Cependant, il est possible de définir une classe qui se comporte comme un type valeur de la même manière que Java.

En ajoutant simplement les données du qualificatif à la classe, des méthodes telles que equals, hashCode, toString et copy sont automatiquement générées depuis le début (ces méthodes ne sont pas visibles depuis la source).

Pour C

Comme mentionné précédemment, C # dispose d'un mécanisme pour définir un type de valeur appelé structure.

En outre, un type qui peut être utilisé comme un littéral appelé type anonyme peut également être utilisé comme un type valeur car la fonction Equals pour la comparaison de valeurs égales est automatiquement affectée.

À partir de C # 7.0, un type appelé tapple peut être utilisé. Il s'agit également d'un type facile à comparer et à copier la même valeur que le type valeur.

Mesures d'héritage des fonctions virtuelles

Pour Java

Tenter d'hériter d'une classe avec le modificateur final ou remplacer une méthode avec le même modificateur entraînera une erreur de compilation. C'est une fonctionnalité que Java possède depuis le début. Fondamentalement, vous pouvez éviter le problème de «classe de base vulnérable» en ajoutant final à toutes les classes et méthodes.

Avec les annotations introduites dans Java 5.0, il est maintenant recommandé d'ajouter l'annotation Override lors du remplacement d'une méthode. C'est une fonctionnalité ajoutée pour éviter l'erreur de créer involontairement une nouvelle méthode qui ne remplace pas, mais cela a pour effet de rendre la superposition un peu plus lourde.

Le «Java efficace» introduit précédemment recommande la méthode de «choisir la composition plutôt que l'héritage». Au lieu d'hériter d'une classe existante, il donne à la nouvelle classe un champ privé qui référence une instance de la classe existante, et chaque méthode de la nouvelle classe appelle la méthode correspondante de la classe existante qu'elle contient. Le résultat est renvoyé (c'est ce qu'on appelle la délégation). Cela évite le problème de la "classe de base vulnérable". En outre, la plupart des IDE ont des fonctionnalités qui prennent en charge cette méthode. Cependant, cette méthode produit un grand nombre de plaques de chaudière.

Pour Kotlin

Kotlin traite les classes comme finales par défaut. Autrement dit, une classe sans aucun modificateur ne peut pas créer une classe dérivée. Le modificateur open doit être ajouté pour permettre l'héritage.

De même, la méthode est traitée comme finale par défaut. En d'autres termes, une méthode sans modificateur ne peut pas être remplacée. Le modificateur ouvert doit être ajouté pour permettre les remplacements. En outre, lors du remplacement de la méthode dans une classe dérivée, le modificateur de remplacement doit être ajouté (l'annotation de remplacement de la fonction correspondante en Java est facultative).

Kotlin a un support actif pour la méthode «choisir la composition plutôt que l'héritage». Le mot-clé by facilite la délégation de méthodes héritées de l'interface à d'autres classes.

Kotlin a un mécanisme appelé fonctions d'extension comme moyen d'étendre la fonctionnalité des classes sans héritage. Ce n'est pas un remplacement pour l'héritage, et c'est plus restrictif qu'une définition de méthode classique, mais c'est un moyen puissant d'ajouter des méthodes à une classe existante plus tard.

Pour C

En C #, vous pouvez interdire l'héritage d'une classe en ajoutant le modificateur scellé à la classe. Cependant, contrairement à Kotlin, ce n'est pas la valeur par défaut.

Comme Kotlin, les méthodes ne sont pas remplaçables par défaut. Pour être substituable, vous devez ajouter le modificateur virtuel et lorsque vous remplacez la méthode dans une classe dérivée, vous devez ajouter le modificateur de remplacement.

Il n'y a pas de mécanisme de délégation de classe comme Kotlin. Lors de la délégation de classe, vous devez écrire une plaque chauffante comme en Java.

Semblable à Kotlin, il existe un mécanisme appelé méthode d'extension comme méthode d'ajout d'une méthode à une classe existante. Cependant, depuis que cette fonction a été ajoutée en C # 3.0, Kotlin l'a utilisée comme référence pour incorporer un mécanisme appelé fonction d'extension dans les spécifications du langage.

Contre-mesures contre le manque de sécurité NULL

Pour Java

Si vous installez une bibliothèque telle que JSR305 ou Lombok introduite précédemment, vous pouvez utiliser l'annotation Nullable et l'annotation NonNull. Ceux-ci aident à détecter NullPointerException (les spécifications varient d'une bibliothèque à l'autre).

À partir de Java8, la classe Optional peut être utilisée comme classe wrapper pour les types de référence potentiellement nuls. Les méthodes de cette classe font la distinction entre Null et les autres cas, ce qui peut réduire l'occurrence de NullPointerException.

Pour Kotlin

Le système de types de Kotlin fait la distinction entre les types à tolérance nulle (types potentiellement nuls) et les types non tolérants à null (types qui ne tolèrent pas les valeurs nulles), ce qui facilite l'évitement de NullPointerExceition. Si vous déclarez un type (tel que String) dans Kotlin en utilisant le nom du type tel quel, il s'agit d'un type null non autorisé. Un point d'interrogation (tel que String?) Doit être ajouté après le nom du type pour en faire un type tolérant NULL.

Le type non autorisé null n'a pas de valeur null, il ne génère donc pas NullPointerExceition. De plus, Kotlin fournit la syntaxe suivante pour les types à tolérance nulle afin de rendre NullPointerExceition moins susceptible de se produire.

Pour C

À partir de C # 8.0 publié en septembre 2019, des types de référence à tolérance nulle sont disponibles. Comme Kotlin, il s'agit d'une fonctionnalité qui autorise les valeurs nulles dans la valeur de retour d'une variable ou d'une fonction uniquement lorsqu'un point d'interrogation est ajouté après le nom du type. La différence avec Kotlin est que vous ne pouvez utiliser des types de référence à tolérance nulle que lorsque vous activez le contexte d'annotation à tolérance nulle pour la compatibilité avec les versions précédentes.

Chaque C # a un temps d'introduction différent, mais il existe les opérateurs suivants qui prennent NULL en considération.

finalement

Dans son livre, Design and Evolution of C ++, Strawstrap, le concepteur du langage C ++, déclare:

"Comme je l'avais prédit, Java a acquis de nouvelles fonctionnalités au fil des ans, et par conséquent a annulé" l'avantage original "de la simplicité, mais cela n'a pas amélioré les performances. Les langues vendent toujours la "simplicité" et survivent ensuite, augmentant en taille et en complexité pour les applications du monde réel. "

Je pense que c'est exactement le cas. Java et C # sont maintenant des langages assez complexes, et Kotlin, qui en est encore à ses balbutiements, deviendra bientôt un langage complexe. Voilà ce qu'est une langue vivante.

Recommended Posts

Langage Java du point de vue de Kotlin et C #
Comment écrire Scala du point de vue de Java
Veuillez noter la division (division) de java kotlin Int et Int
De Java naissant (3 ans) à Node.js (4 ans). Et l'impression de retourner à Java
Résumons la grammaire Java 8 du point de vue des ingénieurs iOS
[Ruby / Refactoring] Du traitement itératif Ruby tel que Java et C au traitement itératif de type Ruby
À propos de la classification et du concept de Immutable / Mutable / Const / Variable de Java et Kotlin.
Comparaison des types sûrs à tolérance nulle, à tolérance nulle et à tolérance nulle du point de vue de la sécurité nulle
J'ai essayé de traduire la grammaire de R et Java [Mis à jour de temps en temps]
[Java] Récupère MimeType à partir du contenu du fichier avec Apathce Tika [Kotlin]
[Java] La partie déroutante de String et StringBuilder
J'ai comparé les caractéristiques de Java et .NET
Résumer les différences entre l'écriture C # et Java
[Java / Kotlin] Redimensionner en tenant compte de l'orientation de l'image
Différence entre Java, C # et JavaScript (comment trouver le degré d'obésité)
AndroidStudio Faisons référence au C ++ dans le module des autres projets (Java / kotlin)
ArrayList et le rôle de l'interface vu depuis List
La comparaison d'énumération est ==, et equals est bonne [Java]
À propos de l'utilité des monades dans une perspective orientée objet
J'ai résumé les types et les bases des exceptions Java
[Java] [Kotlin] Appeler valueOf et les valeurs de Enum de manière générique
Résumé des bases du langage Java
Avantages et inconvénients de Java
Histoire de remplacement C # et Java
[Note] Sortie Java de la somme des éléments pairs et impairs
Je n'ai pas vraiment compris le comportement de Java Scanner et .nextLine ()
Trouvez la classe d'adresse et le type d'adresse à partir de l'adresse IP avec Java
JSON en Java et Jackson Partie 1 Renvoyer JSON à partir du serveur
Accédez à l'abréviation à partir de 5 exemples de listes Java en italique
Commande pour vérifier le nombre et l'état des threads Java
Les débutants en Java ont brièvement résumé le comportement de Array et ArrayList
Kotlin post- et pré-incrémentation et surcharge des opérateurs (comparaison avec C, Java, C ++)
[Kotlin] Obtenez le constructeur / la méthode Java de KFunction et appelez-le
Corrigez le code de caractère en Java et lisez à partir de l'URL
Réécrire les affectations de langage C de l'université avec Node.js
[Java] Supprimer le nombre spécifié de caractères à la fin de StringBuilder
Jetez un œil à Kotlin dans une perspective Java efficace
[Java] J'ai réfléchi aux mérites et aux utilisations de "interface"
Je vais expliquer la différence entre le développement d'applications Android et le développement d'applications iOS du point de vue des ingénieurs iOS
[Java] Supprimer les éléments de la liste
Différences entre Java "débutant" et Kotlin
[Java] Générer un URI de données à partir d'une chaîne d'octets du contenu du fichier [Kotlin]
Installez le plugin memcached sur MySQL et accédez à partir de Java
[Java] Obtenez les dates des derniers lundi et dimanche dans l'ordre
[Édition Java] Histoire de la sérialisation
Je veux revenir à l'écran précédent avec kotlin et java!
Ceci et cela de JDK
Remarques sur la création de l'environnement de développement de Kotlin et la migration de Java vers Kotlin
Lire les 4 premiers octets du fichier de classe Java et générer CAFEBABE
J'ai essayé de résumer les méthodes de Java String et StringBuilder
Génériques Kotlin pour les développeurs Java
La route de JavaScript à Java
[Java] Comment convertir du type String en type Path et obtenir le chemin
Introduction à Scala du point de vue Java (basique)
L'origine des expressions Java lambda
Crypter avec Java et décrypter avec C #
Trouvez la classe d'adresse et le type d'adresse à partir de l'adresse IP avec Java [décoction n ° 2]
Mettez à jour JAVA vers la dernière version vers 1.8.0_144 (lors du téléchargement à partir du Web et de la mise à jour)
Une collection de phrases qui impressionne le "sentiment différent" de Java et de JavaScript