J'ai ces deux petits programmes:
C
#include <stdio.h>
int main()
{
if (5) {
printf("true\n");
}
else {
printf("false\n");
}
return 0;
}
Java
class type_system {
public static void main(String args[]) {
if (5) {
System.out.println("true");
}
else {
System.out.println("false");
}
}
}
qui rapporte le message d'erreur:
type_system.java:4: error: incompatible types: int cannot be converted to boolean
if (5) {
^
1 error
Ma compréhension
Jusqu'ici, j'ai compris cet exemple comme une démonstration des différents systèmes de types. C est plus faiblement typé et permet la conversion de int en booléen sans erreur. Java est plus fortement typé et échoue, car aucune conversation implicite n'est autorisée.
Par conséquent, ma question: Où ai-je mal compris les choses?
Ce que je ne cherche pas
Ma question n'est pas liée au style de codage incorrect. Je sais que c'est mauvais, mais je m'interroge sur la raison pour laquelle C l'autorise et non sur Java. Par conséquent, je m'intéresse au système de types de la langue, en particulier à sa force.
java
c
type-systems
toogley
la source
la source
int
valeurs. Un exemple plus approprié seraitif (pointer)
.Réponses:
1. C et Java sont des langues différentes
Le fait qu'ils se comportent différemment ne devrait pas être terriblement surprenant.
2. C ne fait aucune conversion de
int
àbool
Comment pourrait-il? C n’avait même pas de
bool
type à convertir avant 1999 . C a été créé au début des années 1970 et enif
faisait partie avant même d'être C, à l'époque où il s'agissait simplement d'une série de modifications apportées à B 1 .if
n'était pas simplementNOP
en C depuis près de 30 ans. Il agissait directement sur les valeurs numériques. Le verbiage dans la norme C ( lien PDF ), même plus d'une décennie après l'introduction debool
s en C, spécifie toujours le comportement deif
(p 148) et?:
(p 100) en utilisant les termes "inégalé à 0" et "égal à 0 "plutôt que les termes booléens" true "ou" false "ou quelque chose de similaire.Idéalement, ...
3. ... les chiffres correspondent exactement aux instructions du processeur.
JZ
etJNZ
sont vos instructions d’assemblage x86 de base pour les branchements conditionnels. Les abréviations sont " J UMP si Z ero" et " J UMP si N ot Z ero". Les équivalents de la PDP-11, où C est issue, sontBEQ
( " B ranch si EQ uel ") etBNE
(" B ranch si N ot E Qual").Ces instructions vérifient si l'opération précédente a généré un zéro ou non et saute (ou non) en conséquence.
4. Java accorde beaucoup plus d'importance à la sécurité que jamais 2
Et, soucieux de la sécurité, ils ont décidé que la limitation
if
àboolean
s valait le coût (tant pour la mise en œuvre d'une telle restriction que pour les coûts d'opportunité qui en résultent).1. B n'a même pas de types du tout. Les langues d'assemblage ne le sont généralement pas non plus. Pourtant, les langages B et d’assemblage parviennent très bien à gérer les branches.
2. Selon les mots de Dennis Ritchie, décrivant les modifications prévues à B qui sont devenues C (c'est moi qui souligne):
la source
C 2011 projet en ligne
Notez que cette clause spécifie uniquement que l'expression de contrôle doit avoir un type scalaire (
char
/short
/int
/long
/ etc.), Pas spécifiquement un type booléen. Une branche est exécutée si l'expression de contrôle a une valeur différente de zéro.Comparez cela avec
Spécification du langage Java SE 8
Java, OTOH, requiert spécifiquement que l'expression de contrôle dans une
if
instruction ait un type booléen.Donc, il ne s'agit pas de typage faible ou fort, mais de ce que chaque définition de langage respective spécifie comme expression de contrôle valide.
modifier
En ce qui concerne la raison pour laquelle les langues sont différentes à cet égard, plusieurs points:
C est dérivé de B, qui est un langage "sans type" - en gros, tout était un mot de 32 à 36 bits (selon le matériel), et toutes les opérations arithmétiques étaient des opérations entières. Le système de type de C était verrouillé un à un, de sorte que ...
C n'avait pas de type booléen distinct jusqu'à la version 1999 du langage. C a simplement suivi la convention B consistant à utiliser zéro pour représenter
false
et non-zéro pour représentertrue
.Java, qui date de 20 à 20 ans, a été conçu spécifiquement pour remédier à certaines des faiblesses de C et C ++. Nul doute que renforcer la restriction sur ce qui pourrait être une expression de contrôle dans une
if
déclaration en faisait partie.Il n'y a aucune raison de s'attendre à ce que deux langages de programmation fassent les choses de la même manière. Même des langages aussi proches que C et C ++ divergent de manière intéressante, de sorte que vous pouvez avoir des programmes C juridiques qui ne sont pas des programmes C ++ légaux, ou qui sont des programmes C ++ légaux mais avec une sémantique différente, etc.
la source
int
versboolean
alors que Java ne le permettait pas. La réponse est qu’il n’ya pas de conversion de ce type en C. Les langues sont différentes parce qu’elles sont différentes.Beaucoup de réponses semblent cibler l'expression d'affectation incorporée qui se trouve dans l'expression conditionnelle. (Bien que ce soit un piège potentiel connu, ce n'est pas la source du message d'erreur Java dans ce cas.)
C’est peut-être parce que le PO n’a pas publié le message d’erreur réel et que le
^
curseur pointe directement sur le=
opérateur de l'affectation.Cependant, le compilateur pointe vers le
=
car c'est l'opérateur qui produit la valeur finale (et donc le type final) de l'expression que voit le conditionnel.Il se plaint de tester une valeur non booléenne, avec le type d'erreur suivant:
Le test des entiers, bien que parfois pratique, est considéré comme un piège que les concepteurs de Java ont choisi d'éviter. Après tout, Java a un vrai type de données booléen , ce que C n’a pas (il n’a pas de type booléen) .
Ceci s'applique également aux pointeurs de test de C pour la valeur null / non-null via
if (p) ...
etif (!p) ...
, ce que Java ne permet pas non plus de demander à un opérateur de comparaison explicite d'obtenir le booléen requis.la source
if
bool b = ...; int v = 5 + b;
. Ceci est différent des langues avec un type booléen complet qui ne peuvent pas être utilisées en arithmétique.int v = 5; float f = 2.0 + v;
sont légaux en C._Bool
est en fait l'un des types entiers standard non signés définis par la norme (voir 6.2.5 / 6).Votre question comporte deux parties:
Pourquoi ne pas convertir Java
int
àboolean
?Cela revient à dire que Java doit être aussi explicite que possible. Il est très statique, très "en face" avec son système de types. Les éléments qui sont automatiquement typés dans d'autres langues ne le sont pas, en Java. Vous devez
int a=(int)0.5
aussi écrire . La conversionfloat
enint
perdrait des informations; même que la conversionint
enboolean
et serait donc sujette à erreur. En outre, ils auraient dû spécifier beaucoup de combinaisons. Bien sûr, ces choses semblent évidentes, mais elles avaient pour but de pécher par excès de prudence.Oh, et comparé à d’autres langages, Java était extrêmement précis dans ses spécifications, car le bytecode n’était pas simplement un détail d’implémentation interne. Ils devraient spécifier toutes les interactions, précisément. Énorme entreprise.
Pourquoi n'accepte
if
pas d'autres types queboolean
?if
pourrait parfaitement être défini comme autoriser d’autres types queboolean
. Il pourrait avoir une définition disant que les éléments suivants sont équivalents:true
int != 0
String
avec.length>0
null
(et nonBoolean
avec une valeurfalse
).null
et dont la méthodeObject.check_if
(inventée par moi juste pour cette occasion) retournetrue
.Ils ne l'ont pas fait; ils n'en avaient pas vraiment besoin et ils voulaient qu'il soit aussi robuste, statique, transparent, facile à lire, etc. Pas de fonctionnalités implicites. De plus, l'implémentation serait assez complexe, je suis sûr, de devoir tester chaque valeur dans tous les cas possibles, donc les performances ont peut-être joué un petit facteur (Java était jadis peu utilisé sur les ordinateurs de cette journée; rappelez-vous Il n’existait pas de compilateurs JIT avec les premières versions, du moins pas sur les ordinateurs que j’avais utilisés à l’époque).
Raison plus profonde
Une raison plus profonde pourrait bien être le fait que Java a ses types primitifs, son système de types est donc partagé entre des objets et des primitives. Peut-être que s'ils avaient évité cela, les choses se seraient passées autrement. Avec les règles données dans la section précédente, ils devraient définir la véracité de chaque primitive de manière explicite (puisque les primitives ne partagent pas une super classe et qu'il n'y a pas de définition précise
null
pour les primitives). Cela se transformerait en un cauchemar, rapidement.Perspective
Eh bien, et au final, c'est peut-être une préférence des concepteurs de langage. Chaque langue semble s'y frayer un chemin ...
Par exemple, Ruby n'a pas de types primitifs. Tout, littéralement tout, est un objet. Ils ont beaucoup de facilité à s'assurer que chaque objet a une certaine méthode.
Ruby recherche la vérité sur tous les types d'objets que vous pouvez lui lancer. Chose intéressante, il n’a toujours pas de
boolean
type (car il n’a pas de primitives) et il n’a pas non plus deBoolean
classe. Si vous demandez quelle classe a la valeurtrue
(facilement disponible avectrue.class
), vous obtenezTrueClass
. Cette classe a effectivement des méthodes, à savoir les 4 opérateurs pour les booléens (| & ^ ==
). Ici,if
considère sa valeur falsey si et seulement si c'estfalse
ounil
(lenull
de Ruby). Tout le reste est vrai. Donc,0
ou""
sont les deux vrais.Il aurait été trivial pour eux de créer une méthode
Object#truthy?
pouvant être mise en œuvre pour n’importe quelle classe et de restituer une vérité individuelle. Par exemple,String#truthy?
aurait pu être implémenté pour être vrai pour des chaînes non vides, ou autre chose. Ils ne l’ont pas fait, même si Ruby est l’antithèse de Java dans la plupart des départements (typage dynamique de canards avec mixin, réouverture de classes, etc.).Ce qui pourrait surprendre un programmeur Perl habitué à
$value <> 0 || length($value)>0 || defined($value)
la vérité. Etc.Entrez SQL avec sa convention qui,
null
à l'intérieur de n'importe quelle expression, la rend automatiquement fausse, quoi qu'il arrive. Donc(null==null) = false
. Dans Ruby,(nil==nil) = true
. Moments heureux.la source
((int)3) * ((float)2.5)
c'est assez bien défini en Java (c'est le cas7.5f
).int
enfloat
perd également des informations en général. Java interdit-il également une telle diffusion implicite?Outre les autres bonnes réponses, j'aimerais parler de la cohérence entre les langues.
Lorsque nous pensons à une instruction if mathématiquement pure, nous comprenons que la condition peut être vraie ou fausse, pas d'autre valeur. Tous les principaux langages de programmation respectent cet idéal mathématique; Si vous donnez une valeur booléenne true / false à une instruction if, vous pouvez vous attendre à voir un comportement intuitif et cohérent tout le temps.
Jusqu'ici tout va bien. C'est ce que Java implémente, et seulement ce que Java implémente.
D'autres langues tentent d'apporter des commodités pour les valeurs non booléennes. Par exemple:
n
est un entier. Définissez maintenantif (n)
le raccourci pourif (n != 0)
.x
est un nombre à virgule flottante. Définissez maintenantif (x)
le raccourci pourif (x != 0 && !isNaN(x))
.p
est un type de pointeur. Définissez maintenantif (p)
le raccourci pourif (p != null)
.s
est un type de chaîne. Maintenant, définirif (s)
pour êtreif (s != null && s != "")
.a
est un type de tableau. Définissez maintenantif (a)
pour êtreif (a != null && a.length > 0)
.Cette idée de fournir des si-tests abrégés semble bonne en surface ... jusqu'à ce que vous rencontriez des différences de conceptions et d'opinions:
if (0)
est traité comme faux en C, Python, JavaScript; mais traité comme vrai dans Ruby.if ([])
est traité comme faux en Python mais vrai en JavaScript.Chaque langue a ses propres bonnes raisons de traiter les expressions d’une manière ou d’une autre. (Par exemple, les seules valeurs de fausseté dans Ruby sont
false
etnil
, par conséquent,0
sont la vérité.)Java a adopté une conception explicite pour vous obliger à fournir une valeur booléenne à une instruction if. Si vous avez rapidement traduit du code de C / Ruby / Python vers Java, vous ne pouvez pas laisser les tests if lax inchangés; vous devez écrire explicitement la condition en Java. Prendre un moment pour faire une pause et réfléchir peut vous éviter des erreurs négligentes.
la source
x != 0
c'est la même chosex != 0 && !isNaN(x)
? En outre, c'est normalements != null
pour les pointeurs, mais autre chose pour les non-pointeurs.Eh bien, chaque type scalaire en C, pointeur, booléen (depuis C99), nombre (virgule flottante ou non) et énumération (en raison de la correspondance directe avec des nombres) a une valeur de falsey "naturelle", ce qui est suffisant pour toute expression conditionnelle .
Java en a aussi (même si les pointeurs Java sont appelés des références et sont fortement restreints), mais il a introduit l'auto-boxing dans Java 5.0 qui brouille les eaux de façon inacceptable. En outre, les programmeurs Java exhortent la valeur intrinsèque de la saisie plus fréquente.
L'une erreur qui a donné naissance le débat se limitant des expressions conditionnelles au type booléen est la faute de frappe d'écrire une mission où une comparaison a été prévu, ce qui est pas adressée en limitant le type d'expressions conditionnelles du tout , mais en interdisant l' utilisation d'une nue cession d'expression pour sa valeur.
Tout compilateur moderne en C ou C ++ gère cela facilement, en donnant un avertissement ou une erreur pour de telles constructions douteuses si demandé.
Pour les cas où c'est exactement ce qui était prévu, l'ajout de parenthèses aide.
Pour résumer, restreindre à booléen (et son équivalent encadré en Java) ressemble à une tentative infructueuse de création d'une classe de fautes de frappe provoquée par le choix
=
d'affectation des erreurs de compilation.la source
==
d'opérandes booléens, dont la première est une variable (qui pourrait alors être typée=
),if
est beaucoup moins fréquente à l' intérieur que d'autres types, je dirais, ce n'est donc qu'une tentative semi-échouée.""
,(Object) ""
,0.0
,-0.0
,NaN
, des tableaux vides, etBoolean.FALSE
? Le dernier en particulier est amusant, car c'est un pointeur non nul (true) qui défait unbox. +++ Je déteste aussi écrireif (o != null)
, mais me garder quelques caractères chaque jour et me laisser passer une demi-journée à déboguer mon expression "intelligente" n’est pas une bonne affaire. Cela dit, j'aimerais beaucoup voir un moyen terme pour Java: des règles plus généreuses, mais ne laissant aucune ambiguïté.Les deux objectifs de conception de Java étaient:
Permettez au développeur de se concentrer sur le problème de l'entreprise. Rendez les choses plus simples et moins sujettes aux erreurs, par exemple la récupération de place afin que les développeurs n'aient pas à se concentrer sur les fuites de mémoire.
Portable entre les plates-formes, par exemple, exécuter sur n'importe quel ordinateur avec n'importe quel processeur.
L'utilisation de l'affectation en tant qu'expression était connue pour causer beaucoup de bugs dus aux fautes de frappe, donc sous l'objectif n ° 1 ci-dessus, il n'est pas autorisé de la façon dont vous essayez de l'utiliser.
De plus, déduire que toute valeur non nulle = vraie et toute valeur nulle = faux n'est pas nécessairement portable (oui, croyez-le ou non, certains systèmes traitent 0 comme vrai et 1 comme faux ), donc sous l'objectif n ° 2 ci-dessus, n'est pas autorisé implicitement . Vous pouvez toujours lancer explicitement bien sûr.
la source
À titre d’exemple, c’est ce que font les autres langages: Swift requiert une expression d’un type prenant en charge le protocole "BooleanType", ce qui signifie qu’il doit avoir une méthode "boolValue". Le type "bool" supporte évidemment ce protocole et vous pouvez créer vos propres types le supportant. Les types entiers ne supportent pas ce protocole.
Dans les versions antérieures des langues, les types facultatifs étaient compatibles avec "BooleanType", vous pouviez donc écrire "if x" au lieu de "if x! = Nil". Ce qui rendait l'utilisation d'un "bool facultatif" très déroutant. Un booléen optionnel a les valeurs nil, false ou true et "if b" ne serait pas exécuté si b était nil et exécuté si b était true ou false. Ce n'est plus permis.
Et il semble y avoir un gars qui n'aime pas vraiment ouvrir vos horizons ...
la source