Ce n'est pas pour ton compte en banque, ça ils s'en fichent. C'est pour investir ton argent comme si c'était le leur, sans se faire arnaquer par l'endroit où ils l'investissent.
Ce n'est pas pour ton compte en banque, ça ils s'en fichent. C'est pour investir ton argent comme si c'était le leur, sans se faire arnaquer par l'endroit où ils l'investissent.
N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
"Java c'est mal sécurisé", c'est le nouveau "Java c'est lent".
Un gros mythe qui s'installe au vue des différentes news catastrophiques sur les problèmes de sécurité des applets.
La plupart des gens n'arrivant pas à faire la distinction entre le plugin Java et le langage Java...
a++
Je reste cependant un poil attentif a ce point la.
Depuis le passage au JDK8, outre le fait que parfois la syntaxe des streams peut s’avérer pas trop parlante / difficile a digérer par rapport a de simples boucles, je me suis plusieurs fois repris a devoir simplifier en urgence du code que j'avais écrits pour mes articles en préparation (articles qui sont plus destinés a des débutants / utilisateurs confirmés qu'a des experts).
En effet, j'avais, sur le coup, abusé des lambdas, interfaces fonctionnelle et références de méthode et me trouvais a passer en paramètres de certains traitements devenus très génériques des allocateur, desallocateur, comparateur, etc. Donc, j’étais en train d’écrire en Java une sorte d’équivalents du code, très efficace mais totalement inabordable pour le néophyte, qu'on peut trouver dans ces bon vieux bouquins "The C++ Programming Language" de Stroustrup ou "Effective STL" de Meyers.
Donc j'adore toutes ces nouvelles fonctionnalités mais j'ai de plus en plus l'impression d’écrire du C++ plutôt que du Java
Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.
suivez mon blog sur Développez.
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook
Pour la question du fait que la syntaxe des steams est difficile à digéré par rapport au simples boucles, je ne pense pas. Moi je pense plutôt que c'est une question de maîtrise d'un nouveau paradigme qu'on avait pas en Java. Le même argument peut être donné par un fan du procédural devant l'Objet. Mais une fois on maîtrise l'objet on trouve moche de faire du procédural.
Focaliser son esprit sur les boucles est pour moi n'est pas la bonne chose, car si on se dit que le but du programmeur est d'atteindre ces objectifs, à quoi ça sert vraiment se se focaliser sur le changement d'état que sur le comportement que j'attend de mon programme. Avec la programmation impérative je me soucis plus sur l’enchaînement des instructions de ma boucle, et le changement d'état de mes variables, que faire une fois j'ai tel donné dans tel valeur, et parfois avec plein d'indices surtout pour les boucles imbriqués dont le soucis est de savoir par quoi je dois initialisé, où la boucle va finir est ce que c'est i<n ou i<=n , j=i+1 ou j=i, j<i ou j<=i, des truc comme ça pour moi je ne pense pas que ça sert vraiment à quelque chose que de rendre la vie du développeur difficile au lieu de se concentrer sur les traitements que mon programme doit réaliser.
La programmation fonctionnelle essaye de nous donner un autre mode de codage que l'impérative, il nous permet de regarder le programme comme un ensemble de comportement(fonctions) que mon programme évalue. Non seulement ça contribue fortement sur la sémantique de mon code du moment où il est simple de comprendre un programme fonctionnelle que des boucles dont il faut plein d'effort pour savoir vraiment le comportement, mais ça offre à mon programme plus de choix sur comment attaquer mon programme (évaluation paresseuses et autres..) et permet aussi de réduire le nombre de lignes de code à écrire, même si ce n'est pas toujours. Mais offre la facilité de l'intégration de la programmation parallèle.
Donc pour moi c'est une question d'habitude et une bonne maîtrise du paradigme fonctionnel et puis c'est tout. Personnellement, je ne retournerais jamais aux boucles en Java que si je n'ai pas de choix. Je ne trouve aucune gêne d'abuser des expressions lambda et des référence de méthode (oui ça ressemble à C++ mais je vois pas la gêne) ils sont bon pour la santé du code que les classes anonymes.
Je ne sais pas vraiment si c'est de cela dont tu parles, mais tu oublies un [gros] truc
En C, il n'y a qu'une seule façon simple de créer des tableaux avec les crochets []: mais cela crée en gros un bloc contiguë en mémoire.
Donc 1 seule façon de le parcourir de 0 à N.
Mais en POO, les collections sont plus complexes, mais elles sont nettement plus flexibles et nettement plus adaptées aux besoins.
Mais les données ne sont pas contiguës en mémoire. Donc on est obligé de passer par une classe itérateur spécifique à chaque collection parce qu'elle seule sait comment parcourir la collection.
D’ailleurs cela se voit que tu n'as jamais travaillé avec des fichiers (images, vidéos, ...), parce que pour parser les headers, tu es obligé de faire des boucles pour récupérer les différentes informations
Non la première raison des fonctions c'est de regrouper les traitement afin d'être appelés plusieurs fois, et non pas faire des copier/ collé ou des GOTO
En C aussi, tu peux avoir des collections plus complexes qu'un simple tableau, pour définir une collection (liste, pile, etc.) tu n'as même pas besoin d'OO, l'encapsulation et une forme de généricité [1][2] suffisent.
La grosse différence ici entre le C et les langages plus récent (et objet) n'est pas l'objet mais l'existence d'une bibliothèque standard plus riche (et une bibliothèque standard plus riche qu'en C, ce n'est pas bien difficile ;-))
J'ai vraiment du mal à voir le lien entre le parsing d'une en-tête de fichier et les boucles. C'est quand même rarement répétitif une en-tête.
Au passage, la récursion ça existe aussi et est, pour faire simple, encouragée en fonctionnelle. Il y a certes des contraintes techniques derrière mais bon au temps pour l'obligation des boucles.
Le rapport entre la programmation fonctionnelle et le fait de faire des fonctions pour regrouper les traitements appelés plusieurs fois ? Ne confondrais-tu pas procédurale et fonctionnelle ici ?
[1] Y compris, dans le pire des cas, un type de base qu'il s'appelle object ou void*.
[2] Et dans ce cas précis, un bon polymorphisme paramétrique est amplement mieux que de l'OO.
Tu peux presque rajouter le C++ avec le C
Je pensais aux fichiers MP4. Tu as une table de "où se trouvent les chunks" pour chaque piste (si je ne me trompe pas, et même une table de tailles de chunks).
Donc tu as beau avoir des liste/ deque/ map/ ..., il faut faire une jolie boucle
Et aussi, avec le ID3 V2 (si je ne me trompe pas) où les données ne sont pas fixes, mais on te donne la taille avant [même si on n'est pas obligé de lire caractère par caractère]
La seule contrainte c'est la taille de la pile
Tu veux dire procédurale: listing vers fonctions, et fonctionnelle: Objet vers fonctions
Moi je fais beaucoup de C++ et donc les pointeurs de fonctions, les foncteurs, la surcharge des opérateurs, l'absence de notion d'interface (même s'il y a les virtuelles pures) [on a même la notion d'amitié pour accéder d'une fonction/ classe aux parties d'une autre classe] c'est tellement naturel que je dois confondre les termes.
Euh ... Quoi? Ce n'est pas de cela que je parle et je trouve que tu passes vraiment à coté. Quel est la liaison entre le fait de ne pas utiliser des boucles et le fait de laisser les tableaux? L'API Stream de Java 8 n'est pas fait que pour les collection, on peut ouvrir un flux sur un tableau et manipuler comme on veut, pas de copie, tous les éléments du tableaux restent contiguës comme en C. Il n'était pas question de jeter les tableaux à côté.
Moi je dis tout simplement qu'au lieu par exemple de chercher le maximum d'un tableau utiliser une boucle je préfère faire de la programmation fonctionnelle. Donc au lieu d'avoir à faire:
Je peux le traduire exactement comme ça
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 int tab[]=new int[]{12,14,15,17,28,12,13,60,3,4,7}; int max=Integer.MIN_VALUE; for(int i=0;i<tab.length;i++){ max=Math.max(max,tab[i]); };
Bon il y a déjà une méthode pour ça
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 int tab[]=new int[]{12,14,15,17,28,12,13,60,3,4,7}; int max=IntStream.of(tab) .reduce(Integer.MIN_VALUE,Math::max);Et si je veux afficher tous les éléments
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 nt tab[]=new int[]{12,14,15,17,28,12,13,60,3,4,7}; int max=IntStream.of(tab) .max() .getAsInt();
La 2e des chose je n'ai pas dis aussi que je peux tout faire avec du fonctionnel, j'ai dis que je n'utiliserais pas les boucles quand j'ai le choix, et c'est tout.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 IntStream.of(tab).forEach(it->{ out.println(it);}); //vu que la méthode println a besoin d'une seule entrée qui est lélément en cours je peux faire IntStream.of(tab).forEach(out::println);
Est ce que tu comprend ce que je veux dire? Moi je ne suis pas entrain de définir pourquoi une fonction, mais je parle du paradigme fonctionnel, sa philosophie, comment je dois considérer un programme.Non la première raison des fonctions c'est de regrouper les traitement afin d'être appelés plusieurs fois, et non pas faire des copier/ collé ou des GOTO
Oui des entête contenant des "listes", ça existe mais ça reste assez rare.
C'est par contre bien plus fréquent au sein des données ou d'avoir un ensemble d'entête/données dans le cas de fichier conteneur.
Une contrainte oui, la seule non.
L'optimisation de récursion terminale est une optimisation très fréquence; par contre ça contraint d'implémenter sous forme terminale et ça limite aux langages et/ou implémentations qui supporte cette optimisation (et donc limite la portabilité).
Sans cette optimisation, même en restant dans une plage d'utilisation ne dépassant pas la taille de la pile, la récursion à un coût temporel et spatial différent des versions itératives.
Et encore c'est sans parler de l'expressivité et de la proximité avec le domaine modélisé.
Objet et fonctionnel c'est assez orthogonal (on peut avoir de l'objet non fonctionnel, du fonctionnel non objet mais aussi du fonctionnel objet).
Mais je pense que tu ne l'emploies pas le terme "programmation fonctionnelle" dans son sens usuel, comment définis-tu ce terme ?
Le C++ possède certes des traits fonctionnels (et ça va croissant), permet de programmer dans un esprit un peu fonctionnel et il y a même une inspiration fonctionnelle dans la SL, notamment sur les algorithmes (avec des noms peu usuels), mais je ne le qualifierais pas de fonctionnel. Il a clairement une orientation impérative.
Pourquoi aurait un impact sur les performances ? Rien d'empêche le compilateur de transformer un 5.plus(6) en un bête 5 + 6 à la compilation en ByteCode ou bien en langage machine (JIT). Au moins pour les types primitifs. Pour les fonctions plus complexes, il pourrait soit appeler une sorte de méthode statique, soit "inliner" la fonction.
Pareillement, "new" est un concept d'allocation dans le tas (heap) et non sur la pile (stack). Néanmoins on perdrait le concept de non-initialisé/absence de valeur (null).
Néanmoins, j'émet un gros doute sur le fait de rajouter une nouvelle "classe" de type. Les objets et les primitifs cohabitent déjà assez mal. Je préfère les travaux récents qui "permettent" l'allocation des objets sur la pile au lieu du tas lorsque cela est possible sans risque.
il y a aussi :
- Des transpileurs COBOL -> Java. Pour le peu de complexité des programmes COBOL, il ne doit pas y avoir de grosses difficultés en dehors des affreux couples GOTO/PERFORM UNTIL.
- Souvent complètement démotivés, plus intéressés par leurs avantages sociaux antédiluviens et leurs futures pré-retraites. Les banques feraient de grandes économies à former les plus motivés et embaucher des petits jeunes. D'ailleurs c'est ce qu'ils sont entrain de faire, mais pour le moment les nouveaux font encore du COBOL et servent surtout à redynamiser les équipes plus que vieillisantes (moyennes d'âge > 45 ans par endroit)
- Ca tombe bien IBM fournie sa propre JVM qui tourne sur z/OS ainsi que des drivers pour DB2
En revanche pour les JCLs, je ne sais pas trop si c'est transpilable facilement.
Exactement ce à quoi je pensais en lisant la news
C'est surtout que ces types ne sont pas polymorphe (question d'optimisation). Il n'y a pas de résolution de méthode à faire.
Le problème ce sont les conversions implicites dans tous les sens. Je me souviens d'un code qui lit un double depuis la base de données, le stock en Double dans l'entité (objet lié à la base de données), puis il passe en double dans une couche et ainsi de suite sur une dizaine d'appel pour finir en double. Et je suis pas certains que l'autoboxing renvoie toujours une valeur en cache, dans ce cas je compte pas le nombre d'allocations effectuées pour rien ; d'autant plus si la méthode est appelée excessivement souvent !
En Ceylon, ils ont autorisé la redéfinition d'opérateurs en exploitant les opérateurs comme du sucre syntaxique et un couple interface/méthode dédié. Par exemple, on pourrait avoir Summable avec une méthode add. Le code a + b est simplement pré-compilé en a.add(b). Charge ensuite au processus normale de compilation de résoudre la méthode et de vérifier la signature.
Le tas a plusieurs inconvénients : plus long à lire (il faut résoudre l'adresse et éventuellement charger la page mémoire, etc.), plus long à nettoyer (il faut faire intervenir les algorithmes du GC), plus de contrôle (cache, remise en mémoire centrale, etc.). Alors que sur la pile, t'es sûr de ne pas partagé l'info avec un autre thread et une fois la frame consommée tu peux tout détruire.
"+" a un sens en algèbre de Boole. Et l'opérateur existe aussi pour les String !
C'est pourquoi elles sont dans le package "java.lang" Ce package fait partie du langage et n'est pas simplement une API.
Il y tout de même une différence énorme. Ces types ne sont pas polymorphes et ce sont biens des "valeurs", ainsi on considère que la notion d'identité n'existe pas, comme pour les primitifs et contrairement aux objets. Il est donc probable que ces types pourront "jouer" avec les opérateurs mais ce ne seront pas des objets.
Il ne faut donc pas oublier que quand ces objets se retrouveront sur le tas, ils seront dupliqués à chaque affectation et les incidences sur la mémoire seront à prendre en considération. Ce qui m'effraie d'ailleurs c'est qu'on y accore autant d'importance qu'au boxing, c'est-à-dire aucune.
Ou alors "n" versions de la même classe. Par exemple, si tu veux créer interface de map de caractère, tu devras créer une nouvelle interface (et implémentation) si tu veux faire de même avec un autre primitif. Les Atomic* en sont un exemple.
return (100,3) je trouve ça assez moche. On a l'impression de ne pas savoir de quoi on parle.
- "final" et "class" sont à banir à mon avis. Comme pour les "enum", il faut un mot clé spécifique. D'ailleurs les énumérations sont des formes de "value". Mes choix porteraient sur "value" ou "struct".
- Toujours en me basant sur les énumérations, je pense qu'on devrait pouvoir avoir des "constructeurs". Et la signature du(es) constructeur(s) ne devrai(en)t pas créé d'attribut correspondant. La représentation interne pouvant être bien différente de ce qu'on expose. Par exemple, un vecteur peut-être initialisé avec des degrés ou des radiants ; mais on ne choisira qu'une seule représentation pour l'implémenter.
Bon pourquoi pas mais je suis pas fan, je préfère répéter le type et utiliser une minuscule pour le nom du type.
Un exemple parfait ou je trouve que la syntaxe n'est pas judicieuse. C'est tout de même plus lisible : boolean b2= p1 == point(2,4);
Aucun problème d'inférence si on utilisait la syntaxe complète.
Par ailleurs comment inférer sur ce type de signatures :
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 public void faire(point p); public void faire(vecteur v);
Les types des valeurs doivent être finaux et non polymorphes ainsi le "casting" n'existe pas ; à voir si on autorise une "conversion". Comme pour les primitifs.
De la même manière qu'on résout actuellement deux conflits équivalents :
- Deux classes avec le même nom de base
- Un import static qui a le même nom qu'un membre "local" ou un autre import static
La réponse : on utilise le nom complet. Exemple : com.developpez.java.point
C'est bien le choix effectué en C/C++. C'est d'ailleurs tout l'intérêt du final de C/C++ car cela assure que la référence n'est pas mutée et donc qu'on peut y passer l'adresse de "l'objet" passé en paramètre (y compris l'adresse sur la pile). Après si les "values" ne sont pas mutables, la JVM sait qu'elle peut partager les références pour les "traitements" qui ne partagent pas de données de la pile vers le tas.
Par exemple, si je créés une "value type" dans le corps d'une méthode, elle est initialisée dans le tas. Si je veux l'affecter via un "setter" dans un bean, lors du passage par paramètre, la JVM se contente de donner l'adresse sur la pile. Par contre au moment de l'affectation à l'attribut, la valeur sera bien entièrement recopiée dans le tas.
A contrario, si je prends une valeur depuis un attribut (depuis le tas donc), je peux partager la même référence aussi bien sur la pile que le tas.
En revanche, il reste un cas compliqué où je ne pense pas que la JVM puisse beaucoup inférer :La valeur sera copiée deux fois dans le tas.
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 point p = point(1,2); List<point> liste = new ArrayList<>(); liste.add(p); liste.add(p);
Par ailleurs, quand tu fais List<String,String> newList = new LinkedList<>(bigList); tu vas bien créer une grande quantité d'objet pour gérer la grande taille de la liste. Et il ne me viendrait pas à l'idée de créer une matrice en tant que "value" (sachant la gestion spécifique de la mémoire liée à celles-ci).
Dommage ils auraient éventuellement pu délégué à un constructeur par défaut. Et à défaut d'en définir un il pourrait avoir ce comportement. Là potentiellement on créé une valeur qui pourrait être "hors limite". Je m'exprime autrement : imaginons un type de valeur "Entier strictement positif", alors la valeur d'initialisation sera une valeur hors limite. Même si cet état à vocation à être temporaire, j'aime en tant que concepteur que mes pre-conditions soient toujours vraies ; sauf exceptionnellement dans le cas d'un état transitoire MAIS interne.
Hors là l'état transitoire serait induit par la gestion d'un cycle de vie externe. En d'autres mots, la phase "initialisation" du type utilisateur provoque un état, interne à MON type, temporaire et incohérent.
Dans ce cas le problème n'est pas Java mais ton API. Une API peut-être complexe à manipuler si la problématique qu'elle couvre est complexe. Mais rien n'empêche d'en faire une version/surcouche plus simple. C'est d'ailleurs l'enjeu de l'objet. Avoir des "interfaces de communications" (ie méthodes) "simples" alors que les traitements internes peuvent être complexes.
Ou comment redéfinir les patterns FlyWeight, Facade et consort.
Quand je lis ça, je lis juste : "j'ai dû créer un code complexe pour couvrir un algorithme complexe d'un problème complexe". D'un point de vue de programmeur, je n'y vois aucune aberration. D'un point de vue de concepteur ou architecteur de code, ca peut ouvrir une discussion comme étant tout à fait banale. Et pour un concepteur de langage (ou d'outil de manière générale), mon objectif sera la première affirmation : "ca ne doit pas être aberrant pour l'utilisateur".
Pour faire une dernière analogie, ton code peut-être complexe (que se soit justifié ou pas), il en demeure pas moins que l'utilisation du logiciel doit être simple pour l'utilisateur.
Concernant les fonctions récursives :
Ce n'est ni une condition nécessaire, ni suffisante. D'ailleurs tous les travaux autour des langages fonctionnels, reposent sur le principe de la "récursion à gauche" (il me semble que c'est le terme mais pas sûr). Le principe est justement de remplacer de nombreux empilements par une forme de "linéarisation" à l'exécution. De sortes qu'il n'y a pas besoin d'avoir atteint le "bout" de la pile d'appel (et donc d'empiler les appels) pour calculer le résultat.
Ainsi on optimise l'utilisation de la pile, les copies, les allocations, etc. D’où parfois des performances très intéressantes en utilisant conjointement une écriture optimisée, des types non-mutants et la forme "fonctionnelle".
Java : Cours et tutoriels - FAQ - Java SE 8 API - Programmation concurrente
Ceylon : Installation - Concepts de base - Typage - Appels et arguments
ECM = Exemple(reproduit le problème) Complet (code compilable) Minimal (ne postez pas votre application !)
Une solution vous convient ? N'oubliez pas le tag
Signature par pitipoisson
<remarque sans rapport avec la discussion technique>
ayant personnellement dépassé 65 ans la remarque associant une moyenne d'age > 45 ans à "vieillissant'" m'a fait tiquer.
Je raconte toujours l'histoire d'un de mes anciens élèves: ayant dépassé 45 ans et la boite qui l'employait ayant fait faillite il ne trouvait plus d'emploi: "trop vieux"!
de rage il a passé l'aggrégation de maths et l'a eu avec les honneurs du premier coup -> trop vieux?
j'ai aussi formé à Java d'anciens Coboliaques et ils sont comme les plombiers/zingueurs ou les politiciens: il y en a des bons et des mauvais.... on ne peut pas prévoir
Comme dit la chanson de Brassens : "l'age n'y fait rien à l'affaire ...."
</remarque sans rapport avec la discussion technique>
J'ai des principes: je peux toujours trouver une bonne raison pour les contredire .... mais j'ai des principes!
(mon excellent bouquin sur Java : https://eska-publishing.com/fr/livre...822407076.html)
Je dirais que le problème, c'est que les types objets, en Java, peuvent prendre la valeur null. Et que les primitifs, dans leur fonctionnement actuel, en sont incapables sans un wrapper.
Du code cherchant à traiter des entiers selon la dualité primitif/objet, se retrouvera donc à devoir d'une manière ou d'une autre représenter si oui ou non l'objet est null, et si non sa valeur. Ce qui fait de la mémoire à gérer en plus et des vérifications en plus. Alors que l'idée des types primitifs, c'est de voir leur valeur directement sans indirection.
Mais il est très impopulaire en algorithmique générale.
Normal. Dans le code if(isRaining + hasUmbrella) on a l'impression qu'on essaie d'additionner quelque chose, alors que dans le code if(isRaining || hasUmbrella) déjà on voit qu'on additionne rien et après deux jours de formations à la programmation, on sait y voir un OU logique.
Qui est un type objet donc pas primitif, et aucune autre classe n'a cet opérateur. Effectivement, la classe String profite d'un traitement particulier dans le langage, notamment un opérateur +. Les autres classes n'en ont pas.
"Les autres classes" incluant donc BigInteger. On parlait pas d'autre chose au départ.
Ça aurait été souhaitable pour l'idéal, mais incroyablement impactant pour l'existant. Jusque-là l'initialisation n'avait qu'à mettre 0, false ou null, et maintenant on lui demande d'aller chercher et invoquer des constructeurs ? C'est pas le même boulot, c'est pas le même format, c'est pas les mêmes garanties en temps, en espace et en cohérence mémoire ! Parce que bon, on sait bien qu'en réalité, la JVM initialise la mémoire à zéro et basta. Ça fait 0, false et null comme ça doit.
Alors effectivement on aurait préféré, mais damn, le bytecode et sa gestion n'ont juste plus rien à voir, bon courage pour la migration.
N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
Complexe non, c'est même d'une logique assez simple que de créer des tas et des tas de nouvelles micro-classes et interfaces fonctionnelles génériques destinés a des usages bien précis et spécifiques comme le faisait la STL de C++ déjà, bien avant que Java ne s'y mette. Ça n'en reste pas moins illisible pour autant. Or un code illisible n'est pas aisément maintenable, encore plus lorsqu'on le confie a des débutants.
Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.
suivez mon blog sur Développez.
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook
<remarque sans rapport avec la discussion technique>
Loin de moi de vouloir être insultant. Je suis pas statisticien, sociologue, psychologue, etc. mais simple observateur.
Je préfère précisez que je ne pense pas que toutes les personnes < 45 ans sont comme ci, que celles entre 45 et 65 autrement et enfin celles de > 65 encore autrement. Néanmoins, il faut avouer qu'il y a des généralités et qu'elles sont à prendre pour ce qu'elles sont : "En général, blablabla".
En l’occurrence, j'évoquais des cas très concrets d'équipes complètement démotivés car proche de la retraite (désolé si à 45 ans on est plus proche de la retraite qu'à 30) et sur-avantagés. Et que la mixité sociale/culturelle/générationnelle est bénéfique pour une équipe (de développement ou pas). Car ne jetons pas le bébé avec l'eau du bain.
</remarque sans rapport avec la discussion technique>
Et qui dit que la valeur "null" doit être acceptable pour un primitif ? Au pire ca ne fait qu'un état (et de la mémoire) supplémentaire à gérer.
Les deux exemples étaient juste pour souligner que rien ne motive réellement le choix de ne pas permettre la définition d'opérateur sur d'autres type et notemment BigInteger.
J'avoue que c'est pas clean. Mais rien n'empêche d'initialiser tout à 0, puis d'invoquer le constructeur des "valeurs". Néanmoins, je pense que les motivations sont louables :
- Les performances : pas de traitement à localiser et exécuter ;
- C'est à priori un état très temporaire et la variable n'est pas censée être manipulée durant cette phase transitoire ;
- C'est un état supposé connu/admis/compris par le développeur Java qui a donc la charge de prendre les précautions qui s'imposent.
C'est là ou parfois la "logique de construction" du code, puis des méthodes et enfin des classes (pour prendre une approche bottom-up comme dans le cadre d'une refactorisation) aide réellement. Après on discute un peu dans le vide mais je soulignais juste l'idée que si on fait parfois du code brouillon, il suffit de le mettre au propre pour que tout s'illumine.
Un autre point, la lisibilité d'un écrit tient parfois uniquement à l'expérience dans la "construction". Ainsi je pense qu'un code LISP/OCamel/Haskell peut être compliqué à lire au premier abord mais qu'avec l'expérience, il devient évident.
Dans cet ordre d'idée, je me souviens avoir galéré au premier abord à obtenir des résultats avec Prolog ou R. Mais avec un peu de pratique c'est devenu évident et puissant. J'ai remarqué que d'autres avaient le même soucis lors de l'apprentissage du SQL.
Java : Cours et tutoriels - FAQ - Java SE 8 API - Programmation concurrente
Ceylon : Installation - Concepts de base - Typage - Appels et arguments
ECM = Exemple(reproduit le problème) Complet (code compilable) Minimal (ne postez pas votre application !)
Une solution vous convient ? N'oubliez pas le tag
Signature par pitipoisson
Ben la même personne qui a voulu que les types primitifs soient aussi objet. En Java soit on a pas de valeur null, soit on est un type objet, on choisit l'un ou l'autre.
En poussant un peu on pourrait à la rigueur considérer qu'au runtime un type primitif n'a pas le droit de se faire assigner null, tout comme un Object[0] n'a pas le droit de se faire assigner un Integer si le type réel du tableau est Date[]. Mais dans ce cas ça a pas servi à grand-chose, et il faut faire ces vérifications à chaque assignation où la question se pose, d'où perte de performances.
L'intérêt d'être type objet c'est pas de pouvoir se bercer de pensées rassurantes "bon, cette fois je ne manipule que des objets, tout va bien." Dans un langage statiquement typé, l'intérêt d'être type objet c'est que le type est hiérarchisé et peut intervenir dans un code générique au lieu de spécialiste*.
Au moins donc dans la portée de tel code générique, le type primitif doit pouvoir accepter la valeur null, ou au moins l'idée que cette valeur existe ce qui revient au même. Les Wrappers tels qu'ils fonctionnent à présent s'en occupant d'ailleurs très bien, générant des NullPointerException à l'auto-outbox. Au prix d'une large perte de performances.
* On pourrait me rétorquer qu'on pourrait générifier du comportement sur des types primitifs, des choses comme le + ou le max étant applicables à tous les primitifs numériques, qui pourtant ne peuvent pas prendre la valeur null. Ok, et je reconnais que ce serait bougrement utile. mais là on parle pas de les rendre objet, on parle de leur définir un supertype à eux seuls, qui gère les + et max et ce genre de choses, bref fonctionne comme eux et n'est pas de type objet.
Cet état ne s'est pas généré tout seul et ne sert à rien si on ne va jamais regarder ce qui s'y trouve et qu'on utilise seulement ce qu'on avait avant qu'il existe.
Perte de performances donc. Pour l'initialiser, et quand on est bien obligé d'aller le vérifier, raison pour laquelle il existe.
À la rigueur si on change vraiment beaucoup les règles du langage Java, ça ne sera pas très lourd en perte de performances, certes. Mais là en termes de changements c'est pas de la rigolade. Génériques, lambdas ? De la gnognotte.
Si : pour la cohérence du langage, ce n'était possible que pour deux cas exceptionnels : primitifs et String, la liste complète étant concise et connue.
... Après je dis pas que je suis fondamentalement opposé à permettre la définition d'opérateurs dans les types objets, mais la simplicité du langage qu'on retirait en ne le permettant pas, est évidente.
Ouais enfin il faut tout de même aller le chercher, maintenant, ce constructeur, c'est quand même pas rien. Ça change beaucoup de choses aux règles du format de classe et d'allocation d'objet. Et je ne parle pas des outils qui comptaient sur le fait que l'allocation se fasse en O(n), n étant la taille mémoire des champs d'instance et donc typiquement bas et en réalité O(1). Bref en plus clair passer d'une opération atomique du point de vue du thread à une opération non atomique, c'est rude.
N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
Mea Culpa pour l'initialisation des "value" c'était une fausse bonne idée. Au delà des arguments mentionnés, il y a des cas vraiment trop complexes. Et comme je le disais l'initialisation à vide ne devrait pas poser de problèmes ou alors pas plus que maintenant avec les types primitifs et les objets.
Reste donc encore le problème de la nullité ; même si celui-ci a été délégué à une autre étude. De manière générale, je préfère l'approche prise par certains langages (ex: Ceylon) de ne pas considérer "null" comme une valeur universelle (acceptable pour tous les types). Celle-ci devrait être réservé à un type Nullable.
De ce que j'ai pu y penser, il faudrait faire disparaître les wrappers de l'API. En interne, tant qu'on manipule un type précis (non générique), on garde le fonctionnement actuel et la valeur ne peut jamais être null. Autrement, il faut nécessairement "marquer" la nullité :
- Le plus simple (et le fonctionnement actuel) c'est d'utiliser un "wrapper" mais ceci serait invisible pour le programmeur ;
- Deuxième option un peu plus complexe, stocker l'état avec la valeur. Ceci pose le problème de la taille allouée pour gérer normalement une référence. Par exemple si j'ai une classe avec uniquement cet attribut private T element;, pour le moment je sais qu'il me faut un nombre fixe de bit que la taille de l'architecture (32 ou 64 bits). Là, il faudrait allouer au moins un octet (voir 4 ou 8) de plus.
- Dernière option plus complexe, alloué les primitifs/valeurs par paquet et utiliser un "BitSet" pour marquer la nullité.
Pour les deux dernières solutions, il est nécessaire de supprimer la co/contra-variance pour les génériques portant sur des types primitifs/valeurs. Ainsi List<?> liste = new ArrayList<int>(); ou List<long> liste = new ArrayList<int>(); seraient interdits.
Une autre alternative peut-être plus simple que tout cela. Introduire explicitement une hiérarchie alternative à java.lang.Object : java.lang.Value. Ce serait la "classe" parente des types primitifs et value type. Value ne serait pas "nullable". Concernant les génériques deux options :
- Soit il existe un parent commun (ex : java.lang.Data), les génériques s'appliquent par défaut à java.lang.Object et les instances peut-être nullables. Mais on peut également indiqué qu'on utilise indifféremment une valeur ou une référence en faisait T extends Data ou bien un(e) primitif/valeur quelconque T extends Value, mais dans ce cas "null" n'est pas acceptable. Reste à savoir si l'on admet un nouveau mot clé qui pourrait exprimer une "valeur neutre" (ex : "default") : null pour les références, 0 pour les nombres, false pour les booléens, ??? pour les autres valeurs.
- Un générique doit nécessairement s'appliquer soit à une valeur, soit à une référence. Auquel cas, null existe dans un cas mais pas dans l'autre. Mais la question du "default" risque de se poser tout de même. Et cela nécessite au moins de dupliquer toutes les classes génériques (une version pour les références et une autre pour les primitifs/valeurs. Mais au moins la version pour les valeurs est valable pour tous les primitifs/valeurs.
Bien entendu la restriction de la variances des génériques est maintenue.
Qu'en pensez-vous ?
Java : Cours et tutoriels - FAQ - Java SE 8 API - Programmation concurrente
Ceylon : Installation - Concepts de base - Typage - Appels et arguments
ECM = Exemple(reproduit le problème) Complet (code compilable) Minimal (ne postez pas votre application !)
Une solution vous convient ? N'oubliez pas le tag
Signature par pitipoisson
Je vois dans cette initiative un intérêt énorme en rapport avec l'analyse numérique et plus particulièrement le calcul intensif, domaine dans lequel Java pourrait avoir d'énormes points forts (parallélisme, calcul distribué, code optimisé des boucles). Ce domaine a été totalement négligé jusqu'ici, au grand dam de la communauté scientifique qui pourtant avait lancé pas mal d'initiatives pour faire bouger les concepteurs de Java (cf position de James Gosling par rapport à l'implémentation des doubles : http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf ).
Ce mécanisme permettrait l'introduction de types réels bien meilleurs que ceux disponibles pour le moment (précision, détection de dépassement de capacité) ainsi que de types complexes comme les matrices. Il faudrait ajouter encore à cela la surcharge (très encadrée) des opérateurs pour ces types afin de rendre le code scientifique lisible.
On va dans le bon sens.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager