Javaがif(5){…} if Cのような数値条件を厳密に許可しないのはなぜですか?


33

次の2つの小さなプログラムがあります。

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");
       }
   }
}

エラーメッセージを報告します。

type_system.java:4: error: incompatible types: int cannot be converted to boolean
       if (5) {
           ^
1 error

私の理解

これまでのところ、私はこの例を異なるタイプのシステムのデモンストレーションとして理解しました。Cはより弱い型付けであり、エラーなしでintからbooleanへの変換が可能です。暗黙的な会話は許可されないため、Javaはより強く型付けされ、失敗します。

したがって、私の質問:どこで物事を誤解しましたか?

探していないもの

私の質問は、悪いコーディングスタイルとは関係ありません。私はそれが悪いことを知っていますが、なぜCがそれを許可し、Javaが許可しないのか興味があります。したがって、私は言語の型システム、特にその強さに興味があります。


22
@toogley:Javaの型システムについて、特に知りたいことは何ですか?型システムは、言語仕様で禁止されているため、これを許可しません Cがそれを許可し、Javaが両方の言語で受け入れられると考えられるものと関係があるわけではありません。型システムの結果として生じる動作は、原因ではなく、その影響です。
ロバートハーヴェイ

8
@toogley:なぜあなたは何かを誤解したと思いますか?テキストをもう一度読んで、私はあなたの質問が何であるか理解していません。
ドックブラウン

26
実際、これはCの弱い型付けの例ではありません。過去(C89)、Cにはブール型さえありませんでしたint。より適切な例は次のとおりですif (pointer)
ラッフルウィンド

5
これを理解しようとして多くの研究が行われたようには思えないので、私はダウンボットしました。
jpmc26

11
元の質問は編集されたバージョンとは少し異なるようです(これが、コメントと回答の一部が別の質問に回答しているように見える理由です)。現在の形式では、ここには質問がないようです。何を誤解していますか?Javaは、想定どおりに動作しています。
ジェームズリン

回答:


134

1. CとJavaは異なる言語です

それらが異なる振る舞いをするという事実は、驚くほど驚くべきことではありません。

2. Cはからintへの変換を行っていませんbool

どうして?Cには1999年までbool変換する真の型さえありませんでした。Cは1970年代初頭に作成され、Cになる前の一部であり、B 1に対する一連の変更でした。if

ifNOPほぼ30年間、単にCでした。数値に直接作用しました。C標準(PDFリンク)の冗長性は、C にboolsを導入してから10年以上経っても、「0に等しくない」および「0に等しい」という用語を使用してif(p 148)および?:(p 100)の動作を指定しています。ブール用語「true」または「false」または類似のものではなく。

便利に、...

3. ...数字は、たまたまプロセッサの命令が動作するものです。

JZそして、JNZ条件分岐のための基本的なx86アセンブリ命令です。省略形は、「J ero if Z ero」および「J ump if N ot Z ero」です。Cが生成されたPDP-11の同等物は、BEQ(「EQ ualの場合はB牧場」)および(「N ot E qualの場合はB牧場」)です。BNE

これらの指示は、前の操作の結果がゼロかどうかを確認し、それに応じてジャンプ(またはジャンプ)します。

4. Javaは、Cがこれまで行ってきたよりもはるかに安全性を重視しています2

また、安全性を念頭に置いて、sに制限ifすることbooleanはコストに見合うと判断しました(そのような制限を実装することと、その結果生じる機会費用の両方)。


1. Bには型さえもありません。一般に、アセンブリ言語もそうではありません。しかし、B言語とアセンブリ言語は分岐をうまく処理します。

2. デニスリッチーの言葉で、CになったB(強調鉱山)に対する計画された修正を説明するとき:

...文字やバイトのアドレス指定に対処し、今後の浮動小数点ハードウェアに備えるには、タイピングスキームが必要であると思われました。他の問題、特にタイプセーフティとインターフェイスチェックは、それらが後ほど重要になったようには見えませんでした


13
プロセッサ命令に直接マッピングするCのブール式についての良い点。私はそれについて前に考えなかった。
ロバートハーベイ

17
ポイント4は、Cが安全に焦点を当てていることを暗示しているように思われるため、誤解を招くと思います... Cは、基本的にあなたがやりたいことを何でもできるようにします。
TemporalWolf

13
@TemporalWolfあなたは、あたかもCがあなたがやりたいことをさせるのは悪いことであると述べています。私の意見の違いは、Cはプログラマ向けに書かれたものであり、基本的な能力が期待されるということです。Javaはコードモンキー用に作成されており、入力できる場合はプログラミングできます。しばしばひどくひどいが、だれが気にしますか?Java入門クラスの教師がCSマイナーの一部として強制されたとき、「書くのが簡単」だったので、フィボナッチ数を計算するために再帰を使用するのが役立つかもしれないことを指摘しました。それが、私たちが持っているソフトウェアを持っている理由です。
-DRF

25
@DRF Cネットワーキングクラスの教授の1人は、「Cはすべてのピンが引き出された手g弾の箱のようなものだ」と述べました。あなたは多くの力を持っていますが、それはあなたの顔に爆発することが本質的に保証されています。大多数の場合、面倒な価値はありません。また、より高いレベルの言語を使用すると、はるかに生産的になります。PythonをハッキングCビットトゥイドリングにトランスコードして、速度を6桁向上させましたか?はい、はい。しかし、それは例外であり、規則ではありません。私はPythonで1日でCで2週間かかることを達成できます...私はまともなCプログラマーです。
TemporalWolf

11
@DRF大手企業が開発した主流ソフトウェアでのバッファオーバーフローエクスプロイトのof延を考えると、明らかに、基本的な能力を持つプログラマでさえも、立ち直らないとは信じられません。
モニカを

14

C 2011オンラインドラフト

6.8.4.1 ifステートメントの

制約

1 ステートメントの制御式ifはスカラー型でなければなりません。

セマンティクス

2両方の形式で、式が0と等しくない場合、最初のサブステートメントが実行されます。式でelseは、式が0と等しい場合、2番目のサブステートメントが実行されます。実行されません。

3 else構文で許可されている場合、An は字句的に最も近い先行に関連付けられます。

この節は、制御式がスカラー型(char/ short/ int/ long/ etc。)であることのみを指定し、具体的にはブール型ではないことに注意してください。制御式にゼロ以外の値がある場合、分岐が実行されます。

それと比較してください

Java SE 8言語仕様

14.9 if声明文では、文の条件付き実行または二つの文の条件付き選択、実行する1つまたは他のではなく、両方を可能にします。

if
    IfThenStatement:
        if(Expressionステートメント

    IfThenElseStatement:
        if(ExpressionStatementNoShortIf else ステートメント

    IfThenElseStatementNoShortIf:
        if(ExpressionStatementNoShortIf else StatementNoShortIf
発現はタイプを持たなければならないbooleanBoolean、コンパイル時エラーが発生します。

Java、OTOHでは、ステートメント内の制御式がブール型であることが特に必要ですif

ですから、弱いタイピングと強いタイピングではなく、それぞれの言語定義が有効な制御式として指定するものです。

編集

なぜ言語は、いくつかの点、この特定の点で異なっています:

  1. Cは「タイプレス」言語であるBから派生しました。基本的には、すべてが32ビットから36ビットのワード(ハー​​ドウェアに依存)であり、すべての算術演算は整数演算でした。Cの型システムは、少しずつボルトで固定されていたため、...

  2. Cには、1999バージョンの言語まで明確なブール型がありませんでした。Cは単に、ゼロを使用して表現しfalse、非ゼロを使​​用して表現するBの規則に従いましたtrue

  3. Javaは、数十年前にCをポストデートし、CおよびC ++の欠点のいくつかに対処するために特別に設計されました。間違いなく、ifステートメント内の制御式の制限を厳しくしたこともその一部でした。

  4. 2つのプログラミング言語が同じことを行うことを期待する理由はありません。CやC ++と密接に関連する言語でさえ、いくつかの興味深い方法で分岐します。たとえば、正当なC ++プログラムではない、または正当なC ++プログラムであるがセマンティクスが異なる正当なCプログラムを持つことができます。


残念ながら、2つの回答を「承認済み」としてマークすることはできません
。– toogley

1
はい、これは質問の良い言い直しです。しかし、この質問は、なぜ 2つの言語にこのような違いがあるのかと尋ねました。あなたはこれに全く対処していません。
ダウードはモニカ回復言う

@DawoodibnKareem:問題は、JavaができないintのにCからへの変換を許可した理由booleanでした。答えは、Cにはそのような変換はないということです。言語は異なる言語であるため、言語は異なります。
ジョンボード

はい、「弱いタイピングと強いタイピングの関係ではありません」。オートボクシングとオートアンボクシングを見てください。Cにこれらがある場合、スカラー型も許可できません。

5

答えの多くは、条件式内にある埋め込み割り当て式をターゲットにしているようです。(それは既知の潜在的な落とし穴ですが、この場合のJavaエラーメッセージの原因ではありません。)

これはおそらく、OPが実際のエラーメッセージを発行しておらず、^キャレット=が代入演算子のを直接指しているためです。

ただし、コンパイラは=、条件式が認識する式の最終値(したがって最終型)を生成する演算子であるため、ポインタを指します。

次の種類のエラーで、非ブール値のテストについて不満を言っています。

エラー:互換性のない型:intはブール値に変換できません

整数のテストは便利な場合もありますが、Java設計者が避けることを選択した潜在的な落とし穴と見なされます。結局のところ、Javaには真のブールデータ型があり、Cにはありませ(ブール型はありません)

これは、if (p) ...およびを介したnull /非nullのCのテストポインターにも適用されますif (!p) ...。Javaでは、同様に、必要なブール値を取得するために明示的な比較演算子を必要としません。


1
Cにブール型があります。しかし、それはif声明よりも新しい。
–MSalters

3
@MSalters Cのboolはまだ隠れた整数であるため、できますbool b = ...; int v = 5 + b;。これは、算術では使用できない完全なブール型の言語とは異なります。
ジュール

Cのブール値は非常に小さな整数です。「true」と「false」は、マクロなどで簡単に定義できます。
オスカースコグ

4
@Jules:Cは型安全性が弱いことを指摘しているだけです。ブール型整数値に変換されるからといっ、それ整数型であることを意味するわけでありません。ロジックでは、整数型はint v = 5; float f = 2.0 + v;Cで有効なので浮動小数点型になります。
MSalters17年

1
@MSaltersは、実際に_Bool標準で定義されている標準の符号なし整数型の 1つです(6.2.5 / 6を参照)。
ルスラン

2

互換性のない型:intはbooleanに変換できません

私は、Cがそれを許可し、javaが許可しない理由に興味があります。したがって、私は言語の型システム、特にその強さに興味があります。

質問には2つの部分があります。

Javaが変換さ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(およびBooleanwith値ではない)他のオブジェクト参照false
  • または:以外であり、nullそのメソッドObject.check_if(この機会に私が発明した)が返す他のオブジェクト参照も返しますtrue

彼らはしませんでした。本当の必要はありませんでしたし、彼らはそれをできるだけ堅牢で、静的で、透明で、読みやすいものにすることを望んでいました。暗黙的な機能はありません。また、実装はかなり複雑で、すべての可能なケースで各値をテストする必要があると確信しているため、パフォーマンスも小さな要因を果たしているだけかもしれません最初のリリースではJITコンパイラはありませんでした。少なくとも私が使用していたコンピューターではそうではありませんでした)。

より深い理由

より深い理由は、Javaにプリミティブ型があるため、オブジェクトとプリミティブの間で型システムが引き裂かれていることです。たぶん、もし彼らがそれらを避けていたら、事態は別の方法に変わっていただろう。前のセクションで示したルールでは、すべてのプリミティブの真実性を明示的に定義する必要があります(プリミティブはスーパークラスを共有せず、プリミティブには明確に定義さnullれていないため)。これはすぐに悪夢に変わります。

見通し

まあ、そして最終的に、多分それは言語設計者の好みにすぎないかもしれません。各言語はそこで独自の方法でスピンするようです...

たとえば、Rubyにはプリミティブ型がありません。すべて、文字通りすべてがオブジェクトです。すべてのオブジェクトに特定のメソッドがあることを確認するのは非常に簡単です。

Rubyは、投げることができるすべての種類のオブジェクトの真実性を探します。興味深いことに、それはまだ何もありませんboolean(それはプリミティブを持っていないため)タイプを、そしてそれには持っていないBooleanのいずれかのクラスを。値trueがどのクラスであるか(を使用するtrue.classと便利です)を尋ねると、が得られTrueClassます。そのクラスには実際にメソッド、つまりブール値(| & ^ ==)の4つの演算子があります。ここでは、ifそれはどちらかである場合にのみ、その値のfalseyを考慮しfalseたりnilnullルビーの)。それ以外はすべて真実です。だから、0または""両方が真実です。

Object#truthy?任意のクラスに実装できるメソッドを作成し、個々の真実性を返すのは簡単なことでした。たとえば、String#truthy?空でない文字列、またはその他の場合に当てはまるように実装できます。Rubyはほとんどの部門でJavaのアンチテーゼであるにもかかわらず、そうではありませんでした(mixinを使用した動的なダックタイピング、クラスの再オープンなど)。

これは$value <> 0 || length($value)>0 || defined($value)、真実であることに慣れているPerlプログラマにとっては驚くかもしれません。等々。

SQLを入力します。その規則でnullは、式の内部で何があっても自動的にfalseになります。だから(null==null) = false。Rubyで、(nil==nil) = true。幸せな時間。


実際、((int)3) * ((float)2.5)Javaではかなり明確に定義されています(です7.5f)。
パエロエベルマン

そうです、@PaŭloEbermann、その例を削除しました。
-AnoE

うわあ...ダウンボッターと実際に答えを削除することを投票した人からのコメントをいただければ幸いです。
-AnoE

実際にからintに変換するとfloat、一般に情報も失われます。Javaはそのような暗黙のキャストも禁止していますか?
ルスラン

@Ruslan no(long→doubleに同じ)–潜在的な情報損失は最も重要でない場所でのみ発生し、それほど重要ではないと考えられる場合(非常に大きな整数値)でのみ発生するという考えだと思います。
パエロエベルマン

1

他の素晴らしい答えに加えて、言語間の一貫性についてお話したいと思います。

数学的に純粋なif文について考えるとき、条件はtrueまたはfalseのいずれかであり、他の値はないことを理解しています。すべての主要なプログラミング言語は、この数学的理想を尊重します。ifステートメントにブール値の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でのみfalsy値は、false及びnil従って、0真実です。)

Javaは明示的な設計を採用して、if文にブール値を強制的に提供します。コードをC / Ruby / PythonからJavaに急いで変換した場合、緩いif-testを変更しないでおくことはできません。Javaで条件を明示的に記述する必要があります。少し時間を取って考えると、ずさんな間違いからあなたを救うことができます。


1
x != 0それと同じことを知っていx != 0 && !isNaN(x)ますか?また、通常s != nullはポインター用ですが、非ポインター用です。

@Deduplicatorはどの言語で同じですか?
パエロエベルマン

1
IEEE754を使用して、NaN≠0。NaN≠まったく何でも。(x)が実行され、(!x)も実行される場合
...-gnasher729

0

さて、C、ポインター、ブール(C99以降)、数値(浮動小数点かどうか)および列挙(数値への直接マッピングによる)のすべてのスカラー型には「自然な」偽の値があるため、どのような条件式にも十分です。

Javaにもそれらがあります(Javaポインターが参照と呼ばれ、厳しく制限されている場合でも)が、Java 5.0でオートボクシングが導入され、容認できないほど混乱しています。また、Javaプログラマーは、さらに入力することの本質的な価値を推奨しています。

されるブール型に条件式を制限する比較が意図された割り当ての書き込みミスであるか否かを議論を生み出しつのエラー、しない条件式のタイプを制限することによって対処全てではなく、裸の代入式の使用を許可しないことによりその価値のため。

最新のCまたはC ++コンパイラは、それを簡単に処理し、そのような疑わしい構成要素に対して警告が表示されるか、エラーが表示されます。
意図したとおりの場合には、括弧を追加すると役立ちます。

要約すると、ブール(およびJavaのボックス化された同等物)に制限することは=、割り当てコンパイルエラーを選択することによってタイプミスのクラスを作成しようとして失敗したように見えます。


==ブールオペランドを使用すると、最初のオペランドは変数であり(その後タイプミスする可能性があります=if、他のタイプの場合よりもはるかに少ない頻度で発生するため、それは半失敗の試みにすぎません。
パエロエベルマン

0.0-> falseおよび0.0-> true以外の値は、「自然」に見えません。
gnasher729

@PaŭloEbermann:副作用なしに目的を達成する簡単な方法はありますが、不幸な副次的効果を伴う部分的な結果は、私の本では明らかな失敗です。

他の理由がありません。何のtruthy値について""(Object) ""0.0-0.0NaN、空の配列、およびBoolean.FALSE?特に最後の1つは面白いものです。これは、非ヌルポインター(true)であり、falseに展開されるためです。+++書くことも嫌いif (o != null)ですが、毎日数文字を節約し、「賢い」表現のデバッグに半日費やすことは大したことではありません。そうは言っても、Javaの中間的な方法を見てみたいと思います。より寛大なルールですが、あいまいさはまったく残しません。
-maaartinus

@maaartinus:私が言ったように、Java 5.0での自動ボックス化解除の導入は、ポインターのヌルネスに対応する真理値を割り当てた場合、かなりの問題が発生することを意味しました。しかし、それは自動(アン)ボクシングの問題であり、他の問題ではありません。Cのような自動(非)ボクシングのない言語は、幸いにもそのようなことはありません。

-1

私の質問は、悪いコーディングスタイルとは関係ありません。私はその悪いことを知っていますが、なぜCがそれを許可し、javaが許可しないのか興味があります。

Javaの2つの設計目標は次のとおりです。

  1. 開発者がビジネス上の問題に集中できるようにします。 開発者がメモリリークに集中する必要がないように、ガベージコレクションなど、物事をよりシンプルに、エラーを起こしにくいものにします。

  2. ポータブル例えばプラットフォーム間では、任意のCPUを持つ任意のコンピュータ上で実行します。

式としての割り当ての使用は、タイプミスのために多くのバグを引き起こすことが知られていたので、上記の目標#1では、それを使用しようとしている方法は許可されません。

また、非ゼロ値= trueおよびゼロ値= falseは必ずしも移植可能であるとは限らない(はい、信じるかどうか、一部のシステムは0をtrueとして、1をfalseとして扱う)ため、上記の目標#2では暗黙的に許可されません。もちろん、明示的にキャストすることもできます。


4
Java 8で追加されていない限り、数値型とブール値の間の明示的なキャストはありません。数値をブール値に変換するには、比較演算子を使用する必要があります。ブール値を数値に変換するには、通常、三項演算子を使用します。
ピーターテイラー

これらのタイプミスにより、その値の割り当ての使用を許可しないケースを作成できます。しかし、Javaで== trueおよび== falseが表示される頻度を考慮すると、明らかに制御式を(オプションでボックス化された)ブール型に制限してもそれほど役に立ちません。

Javaは、JavaのゼロとJavaの偽であるため、「一部のシステムは0を真、1を偽として扱う」という移植性を簡単に保証します。OSがそれについてどう考えているかは問題ではありません。
-maaartinus

-1

他の言語が行う例:Swiftでは、「BooleanType」プロトコルをサポートする型の式が必要です。つまり、「boolValue」メソッドが必要です。「bool」タイプは明らかにこのプロトコルをサポートしており、それをサポートする独自のタイプを作成できます。整数型はこのプロトコルをサポートしていません。

古いバージョンの言語では、オプションの型は「BooleanType」をサポートしていたため、「if x!= nil」ではなく「if x」と書くことができました。これにより、「オプションのブール」の使用が非常にわかりにくくなりました。オプションのboolの値はnil、falseまたはtrueで、bがnilの場合は「if b」は実行されず、bがtrueまたはfalseの場合は「if b」が実行されます。これはもう許可されていません。

そして、あなたの視野を開くことを本当に嫌う人がいるようです...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.