Ich habe diese zwei kleinen Programme:
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");
}
}
}
die meldet die Fehlermeldung:
type_system.java:4: error: incompatible types: int cannot be converted to boolean
if (5) {
^
1 error
Mein Verständnis
Bisher habe ich dieses Beispiel als Demonstration der verschiedenen Typsysteme verstanden. C ist schwächer typisiert und ermöglicht die fehlerfreie Konvertierung von int nach boolean. Java ist stärker typisiert und schlägt fehl, da keine implizite Konversation zulässig ist.
Daher meine Frage: Wo habe ich Dinge falsch verstanden?
Was ich nicht suche
Meine Frage bezieht sich nicht auf einen schlechten Codierungsstil. Ich weiß, dass es schlecht ist, aber ich bin daran interessiert, warum C es zulässt und Java nicht. Aus diesem Grund interessiert mich das Typensystem der Sprache, insbesondere ihre Stärke.
java
c
type-systems
toogley
quelle
quelle
int
Werten ausgeführt wurden. Ein besseres Beispiel wäreif (pointer)
.Antworten:
1. C und Java sind verschiedene Sprachen
Die Tatsache, dass sie sich anders verhalten, sollte nicht schrecklich überraschen.
2. C konvertiert nicht von
int
nachbool
Wie könnte es C hatte bis 1999 nicht einmal einen richtigen
bool
Typ zum Konvertieren . C wurde in den frühen 1970er Jahren entwickelt und war Teil davon, noch bevor es C war, als es nur eine Reihe von Modifikationen an B 1 war .if
if
warNOP
fast 30 Jahre lang nicht nur in C. Es hat direkt auf numerische Werte gewirkt. Die Aussprache im C-Standard ( PDF-Link ) gibt auch über ein Jahrzehnt nach Einführung vonbool
s in C das Verhalten vonif
(S. 148) und?:
(S. 100) mit den Begriffen "ungleich 0" und "gleich 0" an "und nicht die booleschen Ausdrücke" wahr "oder" falsch "oder ähnliches.Günstig, ...
3. ... Zahlen entsprechen einfach den Anweisungen des Prozessors.
JZ
undJNZ
sind Ihre grundlegenden x86-Assemblyanweisungen für bedingte Verzweigungen. Die Abkürzungen sind " J ump wenn Z ero" und " J ump wenn N ot Z ero". Die Äquivalente für den PDP-11, aus dem C stammt, sindBEQ
(" B- Ranch, wenn EQ ual") undBNE
(" B- Ranch, wenn N ot E qual").Diese Anweisungen prüfen, ob die vorherige Operation zu einer Null geführt hat oder nicht, und springen (oder nicht) entsprechend.
4. Java legt viel mehr Wert auf Sicherheit als C jemals zuvor 2
Aus Sicherheitsgründen haben sie entschieden, dass die Beschränkung
if
aufboolean
s die Kosten wert ist (sowohl für die Implementierung einer solchen Beschränkung als auch für die daraus resultierenden Opportunitätskosten).1. B hat überhaupt keine Typen. Assemblersprachen tun dies im Allgemeinen auch nicht. B- und Assemblersprachen schaffen es jedoch, Verzweigungen problemlos zu verarbeiten.
2. Mit den Worten von Dennis Ritchie bei der Beschreibung der geplanten Änderungen an B, die zu C wurden (Betonung meiner):
quelle
C 2011 Online-Entwurf
Beachten Sie, dass diese Klausel nur angibt, dass der steuernde Ausdruck einen skalaren Typ (
char
/short
/int
/long
/ etc.) Haben soll , nicht speziell einen booleschen Typ. Eine Verzweigung wird ausgeführt, wenn der steuernde Ausdruck einen Wert ungleich Null hat.Vergleichen Sie das mit
Java SE 8-Sprachspezifikation
In Java (OTOH) muss der steuernde Ausdruck in einer
if
Anweisung einen Booleschen Typ haben.Es geht also nicht um schwache oder starke Typisierung, sondern darum, was die jeweilige Sprachdefinition als gültigen Kontrollausdruck angibt.
Bearbeiten
In Bezug auf die Gründe, warum die Sprachen in dieser Hinsicht unterschiedlich sind, gibt es mehrere Punkte:
C wurde von B abgeleitet, was eine "typenlose" Sprache war - im Grunde war alles ein 32- bis 36-Bit-Wort (abhängig von der Hardware), und alle arithmetischen Operationen waren Ganzzahloperationen. Das Typensystem von C wurde ein wenig nach dem anderen angeschraubt, so dass ...
C hatte bis zur 1999er Version der Sprache keinen bestimmten Booleschen Typ. C folgte einfach der B-Konvention, Null für die Darstellung
false
und Nicht-Null für die Darstellung zu verwendentrue
.Java datiert C um ein paar Jahrzehnte nach und wurde speziell entwickelt, um einige der Mängel von C und C ++ zu beheben. Dazu gehörte zweifellos die Verschärfung der Einschränkung, was ein Kontrollausdruck in einer
if
Anweisung sein könnte.Es gibt keinen Grund zu der Annahme, dass zwei Programmiersprachen die gleichen Schritte ausführen. Sogar Sprachen, die eng mit C und C ++ verwandt sind, weichen auf interessante Weise voneinander ab, so dass Sie legale C-Programme haben können, die keine legalen C ++ - Programme sind oder legale C ++ - Programme, aber mit unterschiedlicher Semantik usw.
quelle
int
nach zuließ ,boolean
während Java dies nicht zuließ . Die Antwort ist, dass es in C keine solche Konvertierung gibt. Die Sprachen sind unterschiedlich, weil sie verschiedene Sprachen sind.Viele der Antworten scheinen auf den eingebetteten Zuweisungsausdruck abzuzielen, der sich im bedingten Ausdruck befindet. (Dies ist zwar eine bekannte Art von potenzieller Gefahr, in diesem Fall ist es jedoch nicht die Quelle der Java-Fehlermeldung.)
Möglicherweise liegt dies daran, dass das OP die eigentliche Fehlermeldung nicht veröffentlicht hat und das
^
Caret direkt auf den=
des Zuweisungsoperators verweist .Der Compiler zeigt jedoch auf,
=
weil der Operator den endgültigen Wert (und damit den endgültigen Typ) des Ausdrucks erzeugt, den die Bedingung sieht.Es beschwert sich über das Testen eines nicht-booleschen Werts mit der folgenden Art von Fehler:
Das Testen von Ganzzahlen ist zwar manchmal praktisch, wird jedoch als potenzielle Gefahr angesehen, die von den Java-Designern vermieden wird. Immerhin hat Java einen echten Booleschen Datentyp , der von C nicht unterstützt wird (es gibt keinen Booleschen Typ) .
Dies gilt auch für die Testzeiger von C für null / non-null via
if (p) ...
undif (!p) ...
, die Java ebenfalls nicht zulässt, stattdessen einen expliziten Vergleichsoperator zu erfordern, um den erforderlichen Booleschen Wert zu erhalten.quelle
if
Aussage.bool b = ...; int v = 5 + b;
. Dies unterscheidet sich von Sprachen mit einem vollständigen Booleschen Typ, der in der Arithmetik nicht verwendet werden kann.int v = 5; float f = 2.0 + v;
in C_Bool
einer der vom Standard definierten vorzeichenlosen Integer- Standardtypen (siehe 6.2.5 / 6).Ihre Frage besteht aus zwei Teilen:
Warum konvertiert Java nicht
int
nachboolean
?Dies läuft darauf hinaus, dass Java so explizit wie möglich sein soll. Es ist sehr statisch, sehr "in deinem Gesicht" mit seinem Typensystem. Dinge, die automatisch in andere Sprachen geschrieben werden, sind in Java nicht so. Du musst auch schreiben
int a=(int)0.5
. Die Konvertierungfloat
inint
würde Informationen verlieren. das gleiche wie konvertierenint
zuboolean
und wäre daher fehleranfällig. Außerdem hätten sie viele Kombinationen angeben müssen. Sicher, diese Dinge scheinen offensichtlich zu sein, aber sie wollten sich auf der Seite der Vorsicht irren.Im Vergleich zu anderen Sprachen war Java in seiner Spezifikation äußerst genau, da der Bytecode nicht nur ein internes Implementierungsdetail war. Sie müssten alle Wechselwirkungen genau spezifizieren. Riesiges Unterfangen.
Warum
if
akzeptiert nicht andere Typen alsboolean
?if
könnte durchaus definiert werden, um andere Typen als zuzulassenboolean
. Es könnte eine Definition haben, die besagt, dass Folgendes äquivalent ist:true
int != 0
String
mit.length>0
null
(und keinBoolean
with-Wertfalse
) ist.null
und deren MethodeObject.check_if
(von mir nur für diesen Anlass erfunden) zurückgibttrue
.Sie taten nicht; Es gab keine wirkliche Notwendigkeit, und sie wollten es so robust, statisch, transparent, einfach zu lesen usw. wie möglich haben. Keine impliziten Funktionen. Außerdem wäre die Implementierung ziemlich komplex, da ich sicher bin, dass jeder Wert auf alle möglichen Fälle getestet werden muss, sodass die Leistung möglicherweise auch einen kleinen Einfluss hat (Java war auf den Computern dieses Tages früher nur langsam; denken Sie daran) Bei den ersten Releases gab es keine JIT-Compiler, zumindest nicht auf den damals verwendeten Computern.
Tieferer Grund
Ein tieferer Grund könnte wohl die Tatsache sein, dass Java seine primitiven Typen hat, weshalb sein Typensystem zwischen Objekten und Primitiven hin und her gerissen ist. Wenn sie diese vermieden hätten, wäre es vielleicht anders gekommen. Mit den im vorherigen Abschnitt angegebenen Regeln müssten sie die Wahrhaftigkeit jedes einzelnen Primitivs explizit definieren (da die Primitive keine Superklasse teilen und es
null
für Primitive keine genaue Definition gibt ). Dies würde sich schnell in einen Albtraum verwandeln.Ausblick
Nun, und am Ende ist es vielleicht nur eine Präferenz der Sprachdesigner. Jede Sprache scheint ihren eigenen Weg dorthin zu finden ...
Zum Beispiel hat Ruby keine primitiven Typen. Alles, buchstäblich alles, ist ein Objekt. Sie haben es sehr leicht, dafür zu sorgen, dass jedes Objekt eine bestimmte Methode hat.
Ruby sucht nach Wahrhaftigkeit für alle Arten von Objekten, die Sie darauf werfen können. Interessanterweise hat es immer noch keinen
boolean
Typ (weil es keine Primitive hat) und es hat auch keineBoolean
Klasse. Wenn Sie fragen, mit welcher Klasse der Werttrue
(handlich verfügbartrue.class
) ist, erhalten SieTrueClass
. Diese Klasse hat tatsächlich Methoden, nämlich die 4 Operatoren für boolean (| & ^ ==
). Hierif
hält seinen Wert Falsey , wenn und nur wenn es entwederfalse
odernil
(dienull
von Ruby). Alles andere ist wahr. Also,0
oder""
sind beide wahr.Es wäre für sie trivial gewesen, eine Methode
Object#truthy?
zu entwickeln, die für jede Klasse implementiert werden kann und eine individuelle Wahrhaftigkeit zurückgibt. BeispielsweiseString#truthy?
könnte implementiert worden sein, um für nicht leere Zeichenfolgen oder so weiter wahr zu sein. Sie taten es nicht, obwohl Ruby in den meisten Abteilungen das Gegenteil von Java ist (dynamisches Duck-Typing mit Mixin, Wiedereröffnung von Klassen und so weiter).Was für einen Perl-Programmierer, der es gewohnt
$value <> 0 || length($value)>0 || defined($value)
ist, die Wahrheit zu sein , vielleicht überraschend ist . Und so weiter.Geben Sie SQL mit der Konvention ein, dass
null
ein Ausdruck in jedem Ausdruck automatisch falsch ist, egal was passiert. Also(null==null) = false
. In Ruby(nil==nil) = true
. Glückliche Zeiten.quelle
((int)3) * ((float)2.5)
ist es in Java ziemlich gut definiert (es ist7.5f
).int
nachfloat
auch Informationen im Allgemeinen. Verbietet Java auch solche impliziten Besetzungen?Zusätzlich zu den anderen guten Antworten möchte ich über die Konsistenz zwischen den Sprachen sprechen.
Wenn wir an eine mathematisch reine if-Anweisung denken, verstehen wir, dass die Bedingung entweder wahr oder falsch sein kann, kein anderer Wert. Jede wichtige Programmiersprache respektiert dieses mathematische Ideal. Wenn Sie einer if-Anweisung einen booleschen true / false-Wert zuweisen, können Sie davon ausgehen, dass Sie stets ein konsistentes, intuitives Verhalten feststellen.
So weit, ist es gut. Dies ist, was Java implementiert, und nur was Java implementiert.
Andere Sprachen versuchen, Bequemlichkeiten für nicht-boolesche Werte zu bringen. Beispielsweise:
n
ist eine ganze Zahl. Definieren Sieif (n)
nun als Abkürzung fürif (n != 0)
.x
handelt sich um eine Gleitkommazahl. Definieren Sieif (x)
nun als Abkürzung fürif (x != 0 && !isNaN(x))
.p
handelt sich um einen Zeigertyp. Definieren Sieif (p)
nun als Abkürzung fürif (p != null)
.s
handelt sich um einen Zeichenfolgentyp. Jetzt definierenif (s)
, um zu seinif (s != null && s != "")
.a
handelt sich um einen Array-Typ. Jetzt definierenif (a)
, um zu seinif (a != null && a.length > 0)
.Diese Idee, Kurzformulierungen für If-Tests bereitzustellen, scheint an der Oberfläche gut zu sein ... bis Sie auf Unterschiede in den Designs und Meinungen stoßen:
if (0)
wird in C, Python, JavaScript als falsch behandelt; aber in Ruby als wahr behandelt.if ([])
wird in Python als false behandelt, in JavaScript jedoch als true.Jede Sprache hat ihre eigenen guten Gründe, um Ausdrücke auf die eine oder andere Weise zu behandeln. (Zum Beispiel sind die einzigen falschen Werte in Ruby
false
undnil
daher0
ist wahr.)Java hat ein explizites Design eingeführt, um Sie zu zwingen, einer if-Anweisung einen Booleschen Wert zuzuweisen. Wenn Sie hastig Code von C / Ruby / Python nach Java übersetzt haben, können Sie keine laxen if-Tests unverändert lassen. Sie müssen die Bedingung explizit in Java ausschreiben. Nehmen Sie sich einen Moment Zeit zum Innehalten und überlegen Sie, wie Sie schlampige Fehler vermeiden können.
quelle
x != 0
ist das selbe wiex != 0 && !isNaN(x)
? Es ist normalerweise auchs != null
für Zeiger, aber etwas anderes für Nicht-Zeiger.Nun, jeder skalare Typ in C, Pointer, Boolean (seit C99), Number (Gleitkomma oder nicht) und Enumeration (aufgrund der direkten Zuordnung zu Zahlen) hat einen "natürlichen" Falsey-Wert, das ist also gut genug für jeden bedingten Ausdruck .
Java hat diese ebenfalls (auch wenn Java-Zeiger als Referenzen bezeichnet werden und stark eingeschränkt sind), hat jedoch Auto-Boxing in Java 5.0 eingeführt, das die Gewässer inakzeptabel durcheinander bringt. Java-Programmierer weisen auch darauf hin, wie wichtig es ist, mehr zu tippen.
Der einzige Fehler, der die Debatte darüber ausgelöst hat, ob bedingte Ausdrücke auf den Booleschen Typ beschränkt werden sollen, ist der Tippfehler, eine Zuweisung zu schreiben, bei der ein Vergleich angestrebt wurde. Dies wird nicht dadurch behoben, dass der Typ der bedingten Ausdrücke überhaupt eingeschränkt wird , sondern die Verwendung eines nackten Zuweisungsausdrucks untersagt wird für seinen Wert.
Jeder moderne C- oder C ++ - Compiler handhabt dies problemlos und gibt bei Fragen eine Warnung oder einen Fehler für solche fragwürdigen Konstrukte aus.
In den Fällen, in denen genau das beabsichtigt ist, hilft das Hinzufügen von Klammern.
Zusammenfassend lässt sich sagen, dass das Beschränken auf Boolesche Werte (und das in Java in Kästchen gesetzte Äquivalent) ein Fehlversuch ist, eine Klasse von Tippfehlern zu
=
erstellen, die durch die Auswahl von Zuweisungskompilierungsfehlern verursacht werden.quelle
==
von booleschen Operanden, von denen der erste eine Variable ist (auf die dann getippt werden könnte=
), in einemif
passiert viel seltener als bei anderen Typen, würde ich sagen, es ist also nur ein halb fehlgeschlagener Versuch.""
,(Object) ""
,0.0
,-0.0
,NaN
, leere Arrays, undBoolean.FALSE
? Besonders das letzte ist lustig, da es sich um einen Nicht-Null-Zeiger (true) handelt, der von false abweicht. +++ Ich hasse es auch zu schreibenif (o != null)
, aber es ist kein gutes Geschäft, mir jeden Tag ein paar Zeichen zu ersparen und mir einen halben Tag Zeit zu lassen, um meinen "cleveren" Ausdruck zu debuggen. Das heißt, ich würde gerne einen Mittelweg für Java sehen: großzügigere Regeln, aber überhaupt keine Mehrdeutigkeit.Zwei der Entwurfsziele von Java waren:
Ermöglichen Sie dem Entwickler, sich auf das Geschäftsproblem zu konzentrieren. Machen Sie Dinge einfacher und weniger fehleranfällig, z. B. Garbage Collection, damit Entwickler sich nicht auf Speicherverluste konzentrieren müssen.
Portabel zwischen Plattformen, zB lauffähig auf jedem Computer mit jeder CPU.
Es war bekannt, dass die Verwendung von Zuordnung als Ausdruck aufgrund von Tippfehlern viele Fehler verursacht. Daher ist es unter Ziel 1 oben nicht zulässig, wie Sie es verwenden möchten.
Daraus zu schließen, dass ein Wert ungleich Null = wahr und ein Wert null = falsch nicht notwendigerweise portabel ist (ja, ob Sie es glauben oder nicht, manche Systeme behandeln 0 als wahr und 1 als falsch ), ist also unter Ziel 2 oben implizit nicht zulässig . Du kannst natürlich trotzdem explizit besetzen.
quelle
Zum Beispiel, was andere Sprachen tun: Swift benötigt einen Ausdruck eines Typs, der das "BooleanType" -Protokoll unterstützt, was bedeutet, dass er eine "boolValue" -Methode haben muss. Der Typ "bool" unterstützt dieses Protokoll offensichtlich und Sie können Ihre eigenen Typen erstellen, die es unterstützen. Ganzzahltypen unterstützen dieses Protokoll nicht.
In älteren Versionen der Sprache unterstützten optionale Typen "BooleanType", sodass Sie "if x" anstelle von "if x! = Nil" schreiben konnten. Was die Verwendung eines "optionalen Bool" sehr verwirrend machte. Ein optionaler Bool hat die Werte null, falsch oder wahr und "wenn b" würde nicht ausgeführt, wenn b null wäre und ausgeführt, wenn b wahr oder falsch wäre. Dies ist nicht mehr erlaubt.
Und es scheint einen Typ zu geben, der es nicht mag, seinen Horizont zu öffnen ...
quelle