次の2つの小さなプログラムがあります。
#include <stdio.h>
int main()
{
if (5) {
printf("true\n");
}
else {
printf("false\n");
}
return 0;
}
class type_system {
public static void main(String args[]) {
if (5) {
System.out.println("true");
}
else {
System.out.println("false");
}
}
}
これはエラーメッセージを報告します:
type_system.Java:4: error: incompatible types: int cannot be converted to boolean
if (5) {
^
1 error
これまでのところ、私はこの例をさまざまな型システムのデモとして理解しました。 Cはより弱い型付けで、エラーなしでintからbooleanへの変換を可能にします。 Javaはより暗黙的に会話が許可されていないため、より強く型付けされて失敗します。
したがって、私の質問:どこで誤解したのですか?
私の質問は悪いコーディングスタイルとは関係ありません。私はそれが悪いことを知っていますが、なぜCが許可するのか、そしてJavaは許可しないのです。したがって、言語の型システム、特にその強みに興味があります。
それらの動作が異なるという事実は、驚くべきことではありません。
int
からbool
への変換を行っていませんどうして? Cは 1999年まで に変換するための真のbool
型さえ持っていませんでした。 Cは1970年代初頭に作成され、if
はCになる前からその一部でしたが、それが単なる一連の変更 [〜#〜] b [〜#〜]1。
if
は、ほぼ30年間、C言語のNOP
ではありませんでした。数値に直接作用しました。 C規格の表現( PDFリンク )は、Cへのbool
sの導入後10年以上経過しても、if
の動作を指定しています(p 148)および?:
(p 100)ブール用語「true」または「false」または類似のものではなく、「0に等しくない」および「0に等しい」という用語を使用します。
便利なことに、...
JZ
およびJNZ
は、条件付き分岐のための基本的なx86アセンブリ命令です。省略形は、「[〜#〜] j [〜#〜] ump if [〜#〜] z [〜#〜] ero」および「[〜#〜] j [〜#〜] ump if [〜#〜] n [〜#〜] ot [〜#〜] z [〜#〜] ero "。 Cが生まれたPDP-11に相当するものはBEQ
( "[〜#〜] b [〜#〜] ranch if [〜#〜] eq [〜#〜] ual ")およびBNE
(" [〜#〜] b [〜#〜] ranch if [〜#〜] n [〜#〜] ot [〜#〜] e [〜#〜] qual ")。
これらの命令は、前の操作がゼロになったかどうかをチェックし、それに応じてジャンプ(またはしない)します。
そして、安全性を念頭に置いて、if
をboolean
sに制限することは、コストに見合う価値があると判断しました(そのような制限を実装することと、結果として生じる機会コストの両方)。
1。 Bには型さえありません。アセンブリ言語も一般的にはそうではありません。しかし、B言語とアセンブリ言語は分岐をうまく処理できます。
2。 Dennis Ritchie の言葉によると、CになったBへの計画された変更を説明するとき(強調は私のもの):
...文字とバイトアドレス指定に対処し、次の浮動小数点ハードウェアに備えるためには、タイピングスキームが必要であるように思われました。その他の問題特にタイプセーフとインターフェイスのチェック当時はそれほど重要ではないように思われましたが後になってから。
6.8.4.1if
ステートメント
制約
1if
ステートメントの制御式は、スカラー型でなければなりません。
セマンティクス
2両方の形式で、式が0と等しくない場合、最初のサブステートメントが実行されます。else
形式では、式が0と等しい場合、2番目のサブステートメントが実行されます。最初のサブステートメントがラベルを介して到達すると、2番目のサブステートメントは実行されません。
3else
は、構文で許可されている場合、語彙的に最も近い先行に関連付けられます。
この句は、制御式がスカラー型(char
/short
/int
/long
/etc。)を持つことのみを指定していることに注意してください。ブール型。制御式にゼロ以外の値がある場合、分岐が実行されます。
それと比較して
14.9if
ステートメントif
ステートメントを使用すると、ステートメントを条件付きで実行したり、2つのステートメントを条件付きで選択して、どちらか一方のみを実行したりできます。IfThenStatement: if(Expression)StatementIfThenElseStatement: if(Expression)StatementNoShortIfelseStatementIfThenElseStatementNoShortIf: if(Expression)StatementNoShortIfelseStatementNoShortIfExpressionの型はboolean
またはBoolean
である必要があります。そうしないと、コンパイル時エラーが発生します。
Java、OTOH、は、if
ステートメントの制御式がブール型を持っていることを具体的に要求します。
つまり、弱い型付けと強い型付けではなく、それぞれの言語定義が有効な制御式として指定するものです。
編集
whyに関しては、言語はこの特定の点で異なります、いくつかの点:
Cは「タイプレス」言語であるBから派生しました-基本的に、すべてが32〜36ビットのWord(ハードウェアに依存)であり、すべての算術演算は整数演算でした。 Cの型システムは、一度に少しずつボルトで固定されていました...
Cには、1999年版の言語が登場するまで、明確なブール型はありませんでした。 Cは単に、Bの慣例に従って、ゼロを使用してfalse
を表し、ゼロ以外を使用してtrue
を表します。
JavaはCを数十年後の日付にしており、CとC++の欠点のいくつかに対処するために特別に設計されました。 if
ステートメントの制御式の可能性に対する制限を強化することは、間違いなくその一部でした。
any2つのプログラミング言語が同じように動作することを期待する理由はありません。 CおよびC++と密接に関連する言語でさえ、いくつかの興味深い方法で分岐しています。たとえば、合法なC++プログラムではない合法的なCプログラム、または合法的なC++プログラムであるが異なるセマンティクスを持つなどがあります。
答えの多くは、条件式内にある埋め込み代入式をターゲットにしているようです。 (これは既知の潜在的な落とし穴ですが、Javaエラーメッセージの原因ではありません。)
これはおそらく、OPが実際のエラーメッセージを公開しなかったためであり、_^
_キャレットが代入演算子の_=
_を直接指しているためです。
ただし、コンパイラーは_=
_を指しています。これは、条件式が参照する式の最終値(つまり最終的な型)を生成する演算子だからです。
次の種類のエラーで、非ブール値のテストについて不満があります。
エラー:互換性のないタイプ:intをブール値に変換できません
整数をテストすることは、時には便利ですが、Java設計者が回避することを選択した潜在的な落とし穴と見なされています。結局、Javaにはtrueブール値データ型、Cが行うnot(ブール型はありません)。
これは、Cのif (p) ...
およびif (!p) ...
を介したnull/non-nullのテストポインターにも適用されます。Javaは、明示的な比較を要求する代わりに同様に許可しません。必要なブール値を取得する演算子。
互換性のない型:intをブール値に変換できません
Cで許可されている理由とJava許可されていない理由に興味があります。したがって、言語の型システム、特にその強みに興味があります。
あなたの質問には2つの部分があります:
int
をboolean
に変換しないのはなぜですか?これは、Javaはできるだけ明示的になるように意図されています。それは非常に静的であり、その型システムでは「あなたの顔に」あります。他の言語で自動的に型キャストされるものは、そうではありません。Javaでは、int a=(int)0.5
も作成する必要があります。float
をint
に変換すると、情報が失われます。つまり、int
をboolean
に変換する場合と同様に、エラーが発生しやすくなります。また、これらを指定する必要がありました。確かに、これらは明白なように見えますが、注意を怠るように意図されています。
ああ、他の言語と比較すると、Javaは非常にでしたが、バイトコードは内部実装の詳細だけではなかったため、正確に指定されていました。すべての相互作用、正確に。
if
がboolean
以外の型を受け入れないのはなぜですか?if
は、boolean
以外のタイプを許可するように完全に定義できます。これは、以下が同等であると定義できます。
true
int != 0
_String
と_.length>0
_null
以外の(およびBoolean
値のfalse
ではない)他のオブジェクト参照。null
でなく、メソッド_Object.check_if
_(この機会に私が開発したもの)である他のオブジェクト参照はtrue
を返します。彼らはしませんでした。本当に必要はありませんでした、そして彼らはそれを可能な限り堅牢で、静的で、透過的で、読みやすいものにしたかったのです。暗黙の機能はありません。また、実装はかなり複雑になると思います。すべての可能なケースについて各値をテストする必要があるので、パフォーマンスも小さな要因を果たしただけかもしれません(Javaはその日のコンピューター上で遅くなっていました。最初のリリースではJITコンパイラではありませんでした。少なくとも、当時使用していたコンピュータではそうではありませんでした)。
より深い理由は、Javaにそのプリミティブ型があるため、その型システムがオブジェクトとプリミティブの間で引き裂かれているという事実である可能性があります。おそらく、それらを回避していれば、事態は別の方法で判明したでしょう。 。前のセクションで示したルールでは、すべての単一プリミティブの真実性を明示的に定義する必要があります(プリミティブはスーパークラスを共有せず、プリミティブのnull
が明確に定義されていないため)これはすぐに悪夢に変わります。
ええ、そして結局のところ、それは言語デザイナーの好みにすぎないのかもしれません。各言語はそこで独自の方法で回転しているようです...
たとえば、Rubyにはプリミティブ型がありません。文字通りすべてがオブジェクトです。すべてのオブジェクトに特定のメソッドがあることを確認するのは非常に簡単です。
Rubyは、投げることができるすべてのタイプのオブジェクトの真実性を探します。興味深いことに、これにはboolean
タイプがまだありません(プリミティブがないため)andBoolean
クラスもありません。 true
の値がどのクラスかを尋ねると(_true.class
_で簡単に利用できます)、TrueClass
を取得します。そのクラスには、実際にはメソッド、つまりブール値の4つの演算子(_| & ^ ==
_)があります。ここで、if
は、false
またはnil
(Rubyのnull
)の場合にのみ、値をfalseと見なします。他のすべては本当です。したがって、_0
_または_""
_はどちらもtrueです。
彼らがメソッド__Object#truthy?
_を作成して、任意のクラスに実装し、個別の真実性を返すことは、簡単なことでした。たとえば、_String#truthy?
_は、空ではない文字列などに対してtrueになるように実装されている可能性があります。 RubyはJavaの対義語です)(ほとんどの部門では動的ダックタイピング、クラスの再オープンなど)にもかかわらず、そうではありませんでした。 。
これは、真実である$value <> 0 || length($value)>0 || defined($value)
に慣れているPerlプログラマーには驚くかもしれません。等々。
どんな式でもnull
が自動的にfalseにするという規則に従ってSQLを入力します。つまり_(null==null) = false
_です。 Rubyでは、_(nil==nil) = true
_。幸せな時間。
他の良い答えに加えて、言語間の一貫性についてお話ししたいと思います。
数学的に純粋なifステートメントについて考えると、条件はtrueまたはfalseのいずれかであり、他の値ではないことがわかります。すべての主要なプログラミング言語はこの数学的理想を尊重します。 if-statementにブール値のtrue/false値を指定すると、常に一貫した直感的な動作が期待できます。
ここまでは順調ですね。これはJavaが実装するものであり、Javaが実装するものだけです。
他の言語は、非ブール値の利便性を提供しようとします。例えば:
n
が整数だとします。次に、if (n)
をif (n != 0)
の省略形として定義します。x
が浮動小数点数であるとします。次に、if (x)
をif (x != 0 && !isNaN(x))
の省略形として定義します。p
がポインター型であるとします。次に、if (p)
をif (p != null)
の省略形として定義します。s
が文字列型であるとします。次に、if (s)
をif (s != null && s != "")
として定義します。a
が配列型であるとします。次に、if (a)
をif (a != null && a.length > 0)
として定義します。簡略版のifテストを提供するというこの考えは、表面的には良いように思えます...デザインや意見の違いに出くわすまで:
if (0)
は、C、Python、JavaScriptではfalseとして扱われます。 Rubyではtrueとして扱われます。if ([])
はPythonではfalseとして扱われますが、JavaScriptではtrueとして扱われます。各言語には、式を何らかの方法で処理する独自の理由があります。 (たとえば、Rubyの偽の値はfalse
とnil
だけなので、0
は真実です。)
Javaは明示的な設計を採用して、ブール値をif文に提供するように強制しました。 C/Ruby/PythonからJavaにコードを急いで変換した場合、緩やかなifテストを変更しないでおくことはできません。 Javaで明示的に条件を書き出す必要があります。少し間を置いて考えると、ずさんな間違いからあなたを救うことができます。
まあ、C、ポインター、ブール(C99以降)、数値(浮動小数点かどうか)、および列挙(数値への直接マッピングによる)のすべてのスカラー型には「自然な」偽の値があるため、条件式には十分です。
Javaにもそれらがあります(Javaポインタは参照と呼ばれ、厳しく制限されています)が、Java 5.0にオートボクシングが導入され、許容範囲を超えて水を混乱させますまた、Javaプログラマーは、さらにタイピングすることの本質的な価値を推奨しています。
条件式をブール型に制限するかどうかの議論を引き起こした1つのエラーは、比較が意図されていた割り当てを記述する誤植ですnot条件式タイプを制限することによって対処まったくですが、その値にネイキッドな代入式を使用することを禁止しています。
最新のCまたはC++コンパイラーはこれを簡単に処理し、要求された場合、このような疑わしい構成要素に対して警告またはエラーを出します。
意図したとおりのケースでは、括弧を追加すると役立ちます。
要約すると、ブール値(およびJavaのボックスで囲まれた同等物)に制限すると、割り当てコンパイルエラーに=
を選択したために、タイプミスのクラスを作成できなかったように見えます。