ทำไม Java ไม่อนุญาตให้ใช้เงื่อนไขตัวเลขเช่นถ้า (5) {…} ถ้า C ทำ?


33

ฉันมีโปรแกรมเล็ก ๆ สองโปรแกรมนี้:

C

#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 เป็นบูลีนโดยไม่มีข้อผิดพลาด Java นั้นพิมพ์และล้มเหลวมากกว่าเพราะไม่อนุญาตให้มีการสนทนาโดยนัย

ดังนั้นคำถามของฉัน: ฉันเข้าใจผิดอยู่ที่ไหน

สิ่งที่ฉันไม่ได้มองหา

คำถามของฉันไม่เกี่ยวข้องกับรูปแบบการเข้ารหัสที่ไม่ดี ฉันรู้ว่ามันไม่ดี แต่ฉันสนใจว่าทำไม C อนุญาตและ Java ไม่ได้ ดังนั้นฉันสนใจระบบพิมพ์ภาษาโดยเฉพาะความแข็งแกร่งของมัน


22
@toogley: ระบบประเภทใน Java ที่คุณต้องการทราบเป็นพิเศษคืออะไร? ระบบประเภทไม่อนุญาตสิ่งนี้เนื่องจากข้อกำหนดภาษาห้ามไว้ เหตุผลที่ C อนุญาตและ Java ไม่มีทุกสิ่งที่เกี่ยวข้องกับสิ่งที่ถือว่ายอมรับได้ในทั้งสองภาษา พฤติกรรมที่เกิดขึ้นของระบบประเภทคือผลของสิ่งนั้นไม่ใช่สาเหตุ
Robert Harvey

8
@toogley: ทำไมคุณคิดว่าคุณเข้าใจผิดบางอย่าง อ่านข้อความอีกครั้งฉันไม่เข้าใจว่าคำถามของคุณคืออะไร
Doc Brown

26
จริงๆแล้วนี่ไม่ใช่ตัวอย่างของการพิมพ์ที่อ่อนแอใน C ในอดีต (C89), C ไม่มีประเภทบูลีนดังนั้นการดำเนินการ "บูลีน" ทั้งหมดจึงดำเนินการกับintค่าจริง ตัวอย่างที่เหมาะสมกว่านั้นก็if (pointer)คือ
Rufflewind

5
ฉันลงคะแนนเพราะดูเหมือนว่าจะไม่มีงานวิจัยมากมายพยายามทำความเข้าใจเรื่องนี้
jpmc26

11
ดูเหมือนว่าคำถามดั้งเดิมที่ถามนั้นแตกต่างจากรุ่นที่แก้ไขเล็กน้อย (ซึ่งเป็นเหตุผลว่าทำไมความคิดเห็นและคำตอบบางข้อดูเหมือนจะตอบคำถามอื่น) ในรูปแบบปัจจุบันดูเหมือนจะไม่มีคำถามที่นี่ เกิดความเข้าใจผิดอะไร Java มีพฤติกรรมตรงตามที่คุณคิดว่าควรจะเป็น
jamesdlin

คำตอบ:


134

1. C และ Java เป็นภาษาอื่น

ความจริงที่ว่าพวกเขามีพฤติกรรมแตกต่างกันไม่ควรแปลกใจอย่างมาก

2. C ไม่ได้ทำการแปลงใด ๆ จากintเป็นbool

เป็นไปได้อย่างไร C ไม่ได้มีจริงboolชนิดแปลงไปจนถึง 1999 C ที่ถูกสร้างขึ้นในช่วงต้นปี 1970 และifเป็นส่วนหนึ่งของมันก่อนที่มันก็ยิ่ง C, กลับมาเมื่อมันเป็นเพียงแค่ชุดของการปรับเปลี่ยนไปB 1

ifไม่ใช่แค่NOPใน C มาเกือบ 30 ปี มันทำหน้าที่โดยตรงกับค่าตัวเลข การใช้คำฟุ่มเฟือยในมาตรฐาน C ( ลิงก์ PDF ) แม้กว่าทศวรรษหลังจากการแนะนำของbools ถึง C ยังคงระบุพฤติกรรมของif(p 148) และ?:(p 100) โดยใช้คำว่า "ไม่เท่ากับ 0" และ "เท่ากับ 0 "แทนที่จะเป็นคำบูลีน" จริง "หรือ" เท็จ "หรือบางอย่างที่คล้ายกัน

สะดวกสบาย ...

3. ... ตัวเลขเกิดขึ้นเมื่อคำสั่งของโปรเซสเซอร์ทำงาน

JZและJNZเป็นคำแนะนำการประกอบ x86 พื้นฐานของคุณสำหรับการแยกย่อยตามเงื่อนไข ตัวย่อคือ " J ump ถ้าZ ero" และ " J ump ถ้าN ot Z ero" เทียบเท่าสำหรับ PDP-11 ที่ C มาเป็นBEQ( " Bไร่ถ้าEQ UAL ') และBNE(' Bไร่ถ้าไม่มี OT E qual")

คำแนะนำเหล่านี้ตรวจสอบว่าการดำเนินการก่อนหน้านี้ส่งผลให้ศูนย์หรือไม่และกระโดด (หรือไม่) ตาม

4. Java ให้ความสำคัญกับความปลอดภัยมากกว่า C เป็นอย่างมาก2

และด้วยความปลอดภัยในใจพวกเขาตัดสินใจว่าการ จำกัดifให้booleans นั้นคุ้มค่ากับค่าใช้จ่าย (ทั้งการดำเนินการตามข้อ จำกัด และต้นทุนโอกาสที่เกิดขึ้น)


1. B ไม่มีประเภทแม้แต่เลย ภาษาแอสเซมบลีโดยทั่วไปก็ไม่เช่นกัน กระนั้นภาษา B และภาษาแอสเซมบลีก็สามารถจัดการกับการแตกแขนงได้อย่างดี

2.ในคำพูดของเดนนิสริตชี่เมื่ออธิบายถึงการปรับเปลี่ยนตามแผนเป็น B ซึ่งกลายเป็น C (เน้นที่เหมือง):

... ดูเหมือนว่ารูปแบบการพิมพ์มีความจำเป็นที่จะต้องรับมือกับตัวละครและการจัดการกับไบต์และเพื่อเตรียมพร้อมสำหรับฮาร์ดแวร์จุดลอยตัวที่จะมาถึง ปัญหาอื่น ๆโดยเฉพาะอย่างยิ่งประเภทความปลอดภัยและการตรวจสอบอินเตอร์เฟสดูเหมือนจะไม่สำคัญเท่าที่ควรในภายหลัง


13
จุดดีเกี่ยวกับนิพจน์บูลีนในการแมป C โดยตรงกับคำสั่งของโปรเซสเซอร์ ฉันไม่เคยคิดมาก่อน
Robert Harvey

17
ฉันคิดว่าจุดที่ 4 นั้นทำให้เข้าใจผิดเนื่องจากดูเหมือนว่า C มีความปลอดภัยเล็ก ๆ น้อย ๆ ... C จะช่วยให้คุณทำอะไรก็ได้ตามที่คุณต้องการ: C สมมติว่าคุณรู้ว่าคุณกำลังทำอะไร
TemporalWolf

13
@TemporalWolf คุณระบุว่าราวกับว่า C อนุญาตให้คุณทำสิ่งที่คุณต้องการไม่ดี ความแตกต่างในความคิดของฉันคือ C เขียนขึ้นสำหรับโปรแกรมเมอร์และคาดว่าจะมีความสามารถขั้นพื้นฐาน Java เขียนขึ้นสำหรับโค้ดลิงและหากคุณสามารถพิมพ์คุณสามารถตั้งโปรแกรมได้ มักจะไม่ดีอย่างน่าประทับใจ แต่ใครจะสนใจล่ะ ฉันจะไม่มีวันลืมเมื่อครูในชั้นเรียนจาวาที่ฉันถูกบังคับให้เป็นส่วนหนึ่งของ CS ผู้เยาว์ของฉันชี้ให้เห็นว่าการใช้การเรียกซ้ำเพื่อคำนวณตัวเลข Fibonnaci อาจเป็นประโยชน์เพราะมันเป็น "เขียนง่าย" นั่นเป็นเหตุผลที่เรามีซอฟต์แวร์ที่เรามี
DRF

25
@DRF หนึ่งในอาจารย์ของฉันสำหรับคลาสเครือข่าย C กล่าวว่า "C เป็นเหมือนกล่องระเบิดมือที่มีหมุดทั้งหมดดึงออกมา" คุณมีพลังมาก แต่ก็รับประกันว่าจะทำให้ใบหน้าของคุณระเบิด ในกรณีส่วนใหญ่ไม่คุ้มกับความยุ่งยากและคุณจะมีประสิทธิผลมากขึ้นด้วยภาษาระดับสูงกว่า ฉันได้แปลงรหัส Python ไปเป็นแฮ็ค C-twiddling เพื่อเพิ่มความเร็วในการสั่งซื้อ 6 ระดับหรือไม่? ใช่ฉันมี แต่นั่นเป็นข้อยกเว้นไม่ใช่กฎ ฉันสามารถทำสำเร็จในหนึ่งวันใน Python สิ่งที่จะใช้เวลาสองสัปดาห์ใน C ... และฉันเป็นโปรแกรมเมอร์ C ที่ดี
TemporalWolf

11
@DRF เนื่องจากความชุกของ buffer overflow exploit ในซอฟต์แวร์กระแสหลักที่พัฒนาโดย บริษัท ชื่อดังเห็นได้ชัดว่าแม้แต่โปรแกรมเมอร์ที่มีความสามารถขั้นพื้นฐานก็ไม่สามารถไว้วางใจได้ว่าจะไม่ยิงเท้าของพวกเขา
Reinstate Monica

14

C 2011 Online Draft

6.8.4.1 if

ข้อ จำกัดคำสั่ง

1 การควบคุมการแสดงออกของifคำสั่งจะต้องมีประเภทสเกลาร์

Semantics

2 ในทั้งสองรูปแบบ substatement แรกจะถูกดำเนินการหากการแสดงออกเปรียบเทียบไม่เท่ากันถึง 0 ในelseรูปแบบ substatement ที่สองจะถูกดำเนินการถ้าการแสดงออกเปรียบเทียบเท่ากับ 0 หากถึงการ substatement แรกผ่านทางฉลาก substatement ที่สองคือ ไม่ดำเนินการ

3 An elseมีความเกี่ยวข้องกับคำศัพท์ที่ใกล้เคียงที่สุดก่อนหน้าหากได้รับอนุญาตจากไวยากรณ์

โปรดทราบว่าข้อนี้ระบุเฉพาะว่าการควบคุมการแสดงออกจะต้องมีประเภทสเกลาร์ ( char/ short/ int/ long/ ฯลฯ .) ไม่ได้เฉพาะประเภทบูลีน สาขาจะถูกดำเนินการถ้าการแสดงออกการควบคุมมีค่าที่ไม่เป็นศูนย์

เปรียบเทียบกับ

ข้อมูลจำเพาะภาษา Java SE 8

14.9 ifคำแถลงคำแถลง

ดังifกล่าวอนุญาตให้ดำเนินการตามเงื่อนไขของข้อความสั่งหรือเลือกเงื่อนไขสองข้อความสั่งดำเนินการอย่างใดอย่างหนึ่ง แต่ไม่ใช่ทั้งสองอย่าง
    ถ้าจากนั้นสถานะ :
        if ( Expression ) Statement

    IfThenElseStatement :
        if ( Expression ) StatementNoShortถ้าอื่นStatement

    IfThenElseStatementNoShortIf :
        if ( Expression ) StatementNoShort if อื่นStatementNoShortIf
การแสดงออกต้องมีประเภทbooleanหรือBooleanหรือรวบรวมข้อผิดพลาดเกิดขึ้นเวลา

Java, OTOH ต้องการการแสดงออกของการควบคุมในifคำสั่งที่มีประเภทบูลีนเป็นพิเศษ

ดังนั้นมันไม่เกี่ยวกับการพิมพ์ที่อ่อนแอและรุนแรง แต่มันเกี่ยวกับความหมายของแต่ละภาษาที่ระบุว่าเป็นนิพจน์ควบคุมที่ถูกต้อง

แก้ไข

สำหรับสาเหตุที่ภาษาแตกต่างกันในเรื่องนี้โดยเฉพาะอย่างยิ่งหลายจุด:

  1. C มาจาก B ซึ่งเป็นภาษา "ไม่มีตัวตน" - โดยทั่วไปทุกอย่างเป็นคำ 32-36- บิต (ขึ้นอยู่กับฮาร์ดแวร์) และการดำเนินการทางคณิตศาสตร์ทั้งหมดเป็นการดำเนินการจำนวนเต็ม ระบบชนิดของ C ถูกปิดในเวลาเช่นนี้ ...

  2. C ไม่มีประเภทบูลีนที่แตกต่างกันจนกระทั่งภาษาปี 1999 C เพียงตามการประชุม B ของการใช้เป็นศูนย์ที่จะเป็นตัวแทนและที่ไม่ใช่ศูนย์ที่จะเป็นตัวแทน falsetrue

  3. Java โพสต์ลงวันที่ C ภายในสองสามทศวรรษที่ดีและได้รับการออกแบบมาโดยเฉพาะเพื่อจัดการข้อบกพร่องบางอย่างของ C และ C ++ ไม่ต้องสงสัยเลยว่าข้อ จำกัด ที่เข้มงวดเกี่ยวกับสิ่งที่อาจเป็นนิพจน์ควบคุมในifข้อความเป็นส่วนหนึ่งของสิ่งนั้น

  4. ไม่มีเหตุผลที่จะคาดหวังไม่ได้ใด ๆสองภาษาโปรแกรมที่จะทำสิ่งต่างๆด้วยวิธีเดียวกัน แม้แต่ภาษาที่เกี่ยวข้องอย่างใกล้ชิดกับ C และ C ++ แตกต่างกันไปในบางวิธีที่น่าสนใจเช่นคุณสามารถมีโปรแกรม C ทางกฎหมายที่ไม่ใช่โปรแกรม C ++ ที่ถูกกฎหมายหรือเป็นโปรแกรม C ++ ที่ถูกกฎหมาย แต่มีความหมายต่างกันเป็นต้น


เศร้าเกินไปฉันไม่สามารถทำเครื่องหมายคำตอบสองคำว่า "ยอมรับ" ..
toogley

1
ใช่นี่เป็นการทบทวนคำถามที่ดีอีกครั้ง แต่คำถามถามว่าทำไมความแตกต่างระหว่างสองภาษานี้ คุณยังไม่ได้พูดเรื่องนี้เลย
Dawood พูดว่าคืนสถานะโมนิก้า

@DawoodibnKareem: คำถามคือสาเหตุที่ C อนุญาตให้แปลงจากintเป็นbooleanขณะที่ Java ไม่ได้ คำตอบคือไม่มีการแปลงใน C. ภาษาที่แตกต่างเพราะพวกเขาเป็นภาษาที่แตกต่างกัน
John Bode

ใช่ "มันไม่เกี่ยวกับการพิมพ์ที่อ่อนแอและรุนแรง" เพียงแค่ดูมวยอัตโนมัติและการแกะกล่องอัตโนมัติ ถ้า C มีสิ่งเหล่านั้นมันก็ไม่อนุญาตสเกลาร์ประเภทใดก็ได้
Deduplicator

5

ดูเหมือนว่าคำตอบหลายคำตอบจะถูกกำหนดเป้าหมายไปที่นิพจน์การมอบหมายที่ฝังอยู่ซึ่งอยู่ในนิพจน์เงื่อนไข (แม้ว่าจะเป็นอันตรายที่อาจทราบได้ แต่ก็ไม่ใช่ที่มาของข้อความแสดงข้อผิดพลาดของ Java ในกรณีนี้)

อาจเป็นเพราะ OP ไม่ได้เผยแพร่ข้อความแสดงข้อผิดพลาดที่เกิดขึ้นจริงและ^เครื่องหมายรูปหมวกจะชี้ไปยัง=ผู้ประกอบการที่ได้รับมอบหมายโดยตรง

อย่างไรก็ตามคอมไพเลอร์กำลังชี้ไปที่=เพราะมันเป็นโอเปอเรเตอร์ที่สร้างค่าสุดท้าย (และเป็นประเภทสุดท้าย) ของนิพจน์ที่เงื่อนไขเห็น

มันกำลังบ่นเกี่ยวกับการทดสอบค่าที่ไม่ใช่บูลีนโดยมีข้อผิดพลาดประเภทต่อไปนี้:

ข้อผิดพลาด: ประเภทที่เข้ากันไม่ได้: int ไม่สามารถแปลงเป็นบูลีนได้

การทดสอบจำนวนเต็มในบางครั้งก็สะดวกถือว่าเป็นข้อผิดพลาดที่ผู้ออกแบบ Java เลือกที่จะหลีกเลี่ยง หลังจากที่ทุกJava มีจริงชนิดข้อมูลแบบบูลซึ่ง C ไม่ได้ (มันไม่มีประเภทบูล)

สิ่งนี้ยังใช้กับพอยน์เตอร์ทดสอบของ C สำหรับโมฆะ / ไม่เป็นโมฆะif (p) ...และif (!p) ...ซึ่งจาวาในทำนองเดียวกันไม่อนุญาตให้ต้องการตัวดำเนินการเปรียบเทียบที่ชัดเจนเพื่อขอรับบูลีนที่ต้องการแทน


1
C จะมีชนิดบูลีน แต่นั่นใหม่กว่าifคำแถลง
MSalters

3
@MSalters bool b = ...; int v = 5 + b;บูลซียังคงเป็นจำนวนเต็มภายใต้ครอบคลุมเพราะฉะนั้นที่คุณสามารถทำได้ สิ่งนี้แตกต่างจากภาษาที่มีบูลีนแบบเต็มซึ่งไม่สามารถใช้ในการคำนวณได้
จูลส์

บูลีนของ C เป็นเพียงจำนวนเต็มเล็กน้อย "true" และ "false" สามารถกำหนดได้อย่างง่ายดายด้วยมาโครหรืออะไรก็ตาม
Oskar Skog

4
@Jules: คุณเพิ่งจะชี้ให้เห็นว่า C มีความปลอดภัยประเภทที่อ่อนแอ เพียงเพราะประเภทบูลีนแปลงเป็นค่าจำนวนเต็มไม่ได้หมายความว่ามันเป็นประเภทจำนวนเต็ม ตามตรรกะของคุณประเภทจำนวนเต็มจะเป็นประเภททศนิยมเนื่องจากint v = 5; float f = 2.0 + v;ถูกต้องตามกฎหมายใน C.
MSalters

1
@MSalters จริง ๆ แล้ว_Boolเป็นหนึ่งในประเภทจำนวนเต็มมาตรฐานที่ไม่ได้ลงนามตามที่กำหนดโดยมาตรฐาน (ดู 6.2.5 / 6)
Ruslan

2

ประเภทที่เข้ากันไม่ได้: int ไม่สามารถแปลงเป็นบูลีนได้

ฉันสนใจว่าทำไม C ถึงยอมและไม่จาวา ดังนั้นฉันสนใจระบบพิมพ์ภาษาโดยเฉพาะความแข็งแกร่งของมัน

คำถามของคุณมีสองส่วน:

ทำไม Java ไม่แปลงintเป็นboolean?

การทำเช่นนี้จะทำให้ Java มีความตั้งใจชัดเจนที่สุดเท่าที่จะเป็นไปได้ มันคงที่ "อยู่ในใบหน้าของคุณ" ด้วยระบบการพิมพ์ สิ่งที่นำแสดงโดยอัตโนมัติในภาษาอื่นไม่ได้เป็นเช่นนั้นใน Java คุณต้องเขียนint a=(int)0.5เช่นกัน การแปลงfloatเป็นintข้อมูลสูญหาย เหมือนกับการแปลงintเป็นbooleanและจะทำให้เกิดข้อผิดพลาดได้ง่าย นอกจากนี้พวกเขาจะต้องระบุชุดค่าผสมจำนวนมาก แน่นอนสิ่งเหล่านี้ดูเหมือนจะชัดเจน แต่พวกเขาตั้งใจที่จะทำผิดในด้านของความระมัดระวัง

โอ้และเมื่อเปรียบเทียบกับภาษาอื่น ๆ แล้ว Java นั้นมีความแม่นยำอย่างมากในสเปคของมันเนื่องจาก bytecode ไม่ได้เป็นเพียงรายละเอียดการติดตั้งภายในเท่านั้น พวกเขาจะต้องระบุการโต้ตอบใด ๆ และทั้งหมดอย่างแม่นยำ กิจการขนาดใหญ่

ทำไมifไม่ยอมรับประเภทอื่นที่ไม่ใช่boolean?

ifbooleanจะสมบูรณ์ดีจะหมายถึงการอนุญาตให้มีชนิดอื่นที่ไม่ใช่ อาจมีคำจำกัดความที่ระบุว่าสิ่งต่อไปนี้เทียบเท่า:

  • true
  • int != 0
  • String กับ .length>0
  • การอ้างอิงวัตถุอื่นใด ๆ ที่ไม่ใช่ - null(ไม่ใช่Booleanค่าที่มีfalse)
  • หรือแม้กระทั่ง: ใด ๆ อ้างอิงวัตถุอื่น ๆ ที่เป็นไม่nullและมีวิธีการObject.check_if(การประดิษฐ์คิดค้นโดยฉันเพียงแค่สำหรับโอกาสนี้) trueผลตอบแทน

พวกเขาไม่ได้; ไม่มีความต้องการที่แท้จริงและพวกเขาต้องการให้มีประสิทธิภาพคงที่โปร่งใสง่ายต่อการอ่านและอื่น ๆ เท่าที่จะทำได้ ไม่มีคุณสมบัติโดยนัย นอกจากนี้การใช้งานจะค่อนข้างซับซ้อนฉันแน่ใจว่าต้องทดสอบแต่ละค่าสำหรับกรณีที่เป็นไปได้ทั้งหมดดังนั้นประสิทธิภาพอาจมีปัจจัยเล็ก ๆ น้อย ๆ เช่นกัน (Java เคยเป็นคนพูดเหลวไหลในคอมพิวเตอร์ของวันนั้นจำไว้ว่ามี ไม่มีคอมไพเลอร์ JIT ที่มีการเปิดตัวครั้งแรกอย่างน้อยก็ไม่ได้ใช้กับคอมพิวเตอร์ที่ฉันใช้)

เหตุผลที่ลึก

เหตุผลที่ลึกกว่านั้นอาจเป็นความจริงที่ว่า Java มีชนิดดั้งเดิมของมันดังนั้นระบบชนิดของมันจึงขาดระหว่างวัตถุและดั้งเดิม บางทีถ้าพวกเขาหลีกเลี่ยงสิ่งเหล่านั้นสิ่งต่าง ๆ ก็จะกลับกลายเป็นอีกทางหนึ่ง ด้วยกฎที่ให้ไว้ในส่วนก่อนหน้าพวกเขาจะต้องกำหนดความจริงของดั้งเดิมทุก ๆอย่างชัดเจน (ตั้งแต่ดั้งเดิมไม่แบ่งปันซูเปอร์คลาสและไม่มีกำหนดชัดเจนnullสำหรับดั้งเดิม) นี่จะกลายเป็นฝันร้ายอย่างรวดเร็ว

ภาพ

และในที่สุดอาจเป็นเพียงความชอบของนักออกแบบภาษา แต่ละภาษาดูเหมือนจะหมุนในแบบของตัวเองที่นั่น ...

ตัวอย่างเช่น Ruby ไม่มีประเภทดั้งเดิม ทุกอย่างแท้จริงทุกอย่างเป็นวัตถุ พวกเขามีเวลาง่ายมากที่จะทำให้แน่ใจว่าวัตถุทุกชิ้นมีวิธีการบางอย่าง

ทับทิมมองหาความจริงในวัตถุทุกประเภทที่คุณสามารถโยนได้ ที่น่าสนใจก็ยังไม่มีbooleanประเภท (เพราะไม่มีพื้นฐาน) และมันก็ไม่มีBooleanคลาส ถ้าคุณถามว่าระดับค่าที่trueมี (คล่องแคล่วสามารถใช้ได้กับtrue.class) TrueClassคุณจะได้รับ ชั้นเรียนนั้นมีวิธีการจริงๆนั่นคือตัวดำเนินการ 4 ตัวสำหรับ booleans ( | & ^ ==) ที่นี่ifพิจารณาค่าของมันปลอมถ้าหากเป็นอย่างใดอย่างหนึ่งfalseหรือnil( nullของ Ruby) ทุกสิ่งทุกอย่างเป็นเรื่องจริง ดังนั้น0หรือ""เป็นจริงทั้งคู่

มันคงเป็นเรื่องไม่สำคัญสำหรับพวกเขาที่จะสร้างวิธีการObject#truthy?ที่สามารถนำไปใช้กับชั้นเรียนใด ๆ และคืนความจริงส่วนบุคคล ตัวอย่างเช่นString#truthy?อาจมีการใช้งานเป็นจริงสำหรับสตริงที่ไม่ว่างเปล่าหรืออะไรก็ตาม พวกเขาไม่ได้แม้ว่า Ruby จะเป็นสิ่งที่ตรงกันข้ามของ Java ในแผนกส่วนใหญ่ (การพิมพ์เป็ดแบบไดนามิกด้วยมิกซ์อิน, การเปิดคลาสอีกครั้งและทั้งหมดนั้น)

ซึ่งอาจจะแปลกใจสำหรับโปรแกรมเมอร์ Perl ที่เคย$value <> 0 || length($value)>0 || defined($value)เป็นจริง และอื่น ๆ

ป้อน SQL ด้วยแบบแผนที่ nullอยู่ภายในนิพจน์ใด ๆ ทำให้เป็นเท็จโดยอัตโนมัติไม่ว่าอะไรก็ตาม (null==null) = falseดังนั้น ในทับทิม, (nil==nil) = true. เวลาแห่งความสุข.


ที่จริงแล้ว((int)3) * ((float)2.5)ค่อนข้างชัดเจนใน Java (เป็น7.5f)
Paŭlo Ebermann

คุณถูกต้อง @ PaŭloEbermannฉันได้ลบตัวอย่างนั้นแล้ว
AnoE

ว้าวที่นั่น ... จะขอบคุณความคิดเห็นจากผู้ลงคะแนนและคนที่โหวตให้ลบคำตอบจริง ๆ
AnoE

จริงๆแล้วการแปลงจากintเป็นfloatสูญเสียข้อมูลโดยทั่วไปด้วย Java ยังห้ามการโยนโดยปริยายเช่นนี้หรือไม่?
Ruslan

@ Ruslan ไม่ (เหมือนกันสำหรับ→ยาวสองเท่า) - ฉันเดาว่าความคิดคือการสูญเสียข้อมูลที่อาจเกิดขึ้นมีเพียงในสถานที่ที่สำคัญน้อยที่สุดและเกิดขึ้นเฉพาะในกรณีที่ถือว่าไม่สำคัญ (ค่าจำนวนเต็มค่อนข้างมาก)
Paŭlo Ebermann

1

นอกเหนือจากคำตอบที่ดีอื่น ๆ ฉันต้องการพูดคุยเกี่ยวกับความสอดคล้องระหว่างภาษา

เมื่อเราคิดถึงคำสั่ง if ที่บริสุทธิ์ทางคณิตศาสตร์เราเข้าใจว่าเงื่อนไขสามารถเป็นจริงหรือเท็จไม่มีค่าอื่น ๆ ภาษาการเขียนโปรแกรมหลักทุกคนเคารพอุดมคติทางคณิตศาสตร์นี้ ถ้าคุณให้ค่าบูลีนจริง / เท็จกับคำสั่ง if แล้วคุณสามารถคาดหวังที่จะเห็นพฤติกรรมที่สอดคล้องและใช้งานง่ายตลอดเวลา

จนถึงตอนนี้ดีมาก นี่คือสิ่งที่ 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 (0)ถือว่าเป็นเท็จใน C, Python, JavaScript; แต่ถือว่าเป็นจริงใน Ruby
  • if ([]) ถือว่าเป็นเท็จใน Python แต่จริงใน JavaScript

แต่ละภาษามีเหตุผลที่ดีในการใช้สำนวนไม่ทางใดก็ทางหนึ่ง (ตัวอย่างเช่นค่าเท็จเท่านั้นใน Ruby คือfalseและnilด้วยเหตุนี้0เป็นความจริง)

Java ใช้การออกแบบที่ชัดเจนเพื่อบังคับให้คุณจัดหาค่าบูลีนให้กับคำสั่ง if หากคุณแปลรหัสจาก C / Ruby / Python เป็น Java อย่างเร่งรีบคุณจะไม่สามารถปล่อยให้หละหลวมหากการทดสอบไม่เปลี่ยนแปลง คุณต้องเขียนเงื่อนไขอย่างชัดเจนใน Java สละเวลาสักครู่เพื่อหยุดและคิดว่าสามารถช่วยคุณจากความผิดพลาดที่เลอะเทอะ


1
คุณรู้หรือไม่x != 0ว่าเป็นเช่นเดียวกันx != 0 && !isNaN(x)? นอกจากนี้ยังเป็นเรื่องปกติs != nullสำหรับตัวชี้ แต่อย่างอื่นสำหรับตัวชี้ที่ไม่ใช่
Deduplicator

@Dupuplicator ในภาษาใดที่เหมือนกัน
Paŭlo Ebermann

1
ใช้ IEEE754, NaN ≠ 0. NaN ≠อะไรก็ได้ ดังนั้นถ้า (x) จะดำเนินการและหาก (! x) จะดำเนินการเช่นกัน ...
gnasher729

0

สเกลาร์ทุกประเภทใน C, ตัวชี้, บูลีน (ตั้งแต่ C99), หมายเลข (floating-point หรือไม่) และการแจงนับ (เนื่องจากการจับคู่โดยตรงกับตัวเลข) มีค่า "ธรรมชาติ" ปลอมดังนั้นจึงดีพอสำหรับการแสดงออกตามเงื่อนไขใด ๆ .

Java มีสิ่งเหล่านั้นด้วย (แม้ว่าตัวชี้ Java จะถูกเรียกว่าการอ้างอิงและถูก จำกัด อย่างหนัก) แต่ก็มีการแนะนำมวยอัตโนมัติใน Java 5.0 ซึ่งทำให้น้ำไม่เป็นที่ยอมรับ นอกจากนี้ผู้เขียนโปรแกรมจาวายังแนะนำคุณค่าที่แท้จริงของการพิมพ์มากขึ้น

ข้อผิดพลาดอย่างหนึ่งที่เกิดขึ้นจากการถกเถียงกันว่าการ จำกัด เงื่อนไขนิพจน์ให้เป็นบูลีนเป็นประเภทของการเขียนการมอบหมายที่การเปรียบเทียบนั้นตั้งใจจะทำซึ่งไม่ได้กล่าวถึงโดยการ จำกัด ประเภทของนิพจน์เงื่อนไขทั้งหมดแต่โดยไม่อนุญาต สำหรับมูลค่าของมัน

คอมไพเลอร์ C หรือ C ++ ที่ทันสมัยใด ๆ จัดการได้อย่างง่ายดายซึ่งให้คำเตือนหรือข้อผิดพลาดสำหรับการสร้างที่น่าสงสัยดังกล่าวหากถูกถาม
สำหรับกรณีที่ตรงตามที่ตั้งใจไว้การเพิ่มวงเล็บช่วย

ในการสรุปการ จำกัด บูลีน (และค่าที่เทียบเท่าใน Java) ดูเหมือนว่าความพยายามที่ล้มเหลวในการสร้างคลาสของความผิดพลาดที่เกิดจากการเลือก=สำหรับการรวบรวมข้อผิดพลาดที่ได้รับมอบหมาย


ใช้==กับตัวถูกดำเนินการบูลีนซึ่งเป็นคนแรกเป็นตัวแปร (ซึ่งสามารถพิมพ์โดย=) ในภายในifเกิดขึ้นน้อยกว่าบ่อยกว่าประเภทอื่น ๆ ฉันจะบอกว่าดังนั้นจึงเป็นเพียงความพยายามกึ่งล้มเหลว
Paŭlo Ebermann

การมี 0.0 -> false และค่าอื่น ๆ ที่ไม่ใช่ 0.0 -> true นั้นไม่ได้ดูเป็นธรรมชาติสำหรับฉันเลย
gnasher729

@ PaŭloEbermann: ผลลัพธ์บางส่วนที่มีผลข้างเคียงที่โชคร้ายในขณะที่มีวิธีง่ายๆในการบรรลุเป้าหมายโดยไม่มีผลข้างเคียงเป็นความล้มเหลวที่ชัดเจนในหนังสือของฉัน
Deduplicator

คุณหายไปด้วยเหตุผลอื่น สิ่งที่เกี่ยวกับค่าของ truthy "", (Object) "", 0.0, -0.0, NaNอาร์เรย์ว่างเปล่าและBoolean.FALSE? โดยเฉพาะอย่างยิ่งคนสุดท้ายเป็นเรื่องตลกเพราะมันไม่ใช่ตัวชี้ (จริง) ซึ่ง unboxes เป็นเท็จ +++ ฉันเกลียดการเขียนif (o != null)แต่การประหยัดตัวอักษรไม่กี่ตัวทุกวันและให้ฉันใช้เวลาครึ่งวันในการดีบั๊กการแสดงออก "ฉลาด" ของฉันก็ไม่ใช่ข้อตกลงที่ดี ที่กล่าวว่าฉันชอบที่จะเห็นกลางทางสำหรับ Java: กฎระเบียบที่ใจกว้างมากขึ้น แต่ไม่มีความคลุมเครือเลย
maaartinus

@ maaartinus: ดังที่ฉันได้กล่าวเอาไว้การแนะนำ auto-unboxing ใน Java 5.0 หมายความว่าถ้าพวกเขากำหนดค่าความจริงที่สัมพันธ์กับ null-ness ต่อตัวชี้พวกเขาจะมีปัญหา แต่นั่นเป็นปัญหาของมวยอัตโนมัติ (ไม่ -) ไม่ใช่อย่างอื่น ภาษาที่ไม่มี auto- (un-) ชกมวยเช่น C ไม่มีความสุขในเรื่องนั้น
Deduplicator

-1

คำถามของฉันไม่เกี่ยวข้องกับรูปแบบการเข้ารหัสที่ไม่ดี ฉันรู้ว่ามันไม่ดี แต่ฉันกำลัง intersted ในเหตุผลที่ C ไม่อนุญาตและ java ไม่

สองเป้าหมายการออกแบบของ Java คือ:

  1. อนุญาตให้ผู้พัฒนามุ่งเน้นไปที่ปัญหาทางธุรกิจ ทำให้สิ่งต่าง ๆ ง่ายขึ้นและมีข้อผิดพลาดน้อยลงเช่นการรวบรวมขยะเพื่อให้นักพัฒนาไม่จำเป็นต้องมุ่งเน้นไปที่หน่วยความจำรั่ว

  2. เคลื่อนย้ายได้ระหว่างแพลตฟอร์มเช่นทำงานบนคอมพิวเตอร์ที่มี CPU ใด ๆ

เป็นที่ทราบกันดีว่าการใช้การมอบหมายเป็นนิพจน์ทำให้เกิดข้อบกพร่องมากมายเนื่องจากการพิมพ์ผิดดังนั้นภายใต้เป้าหมาย # 1 ด้านบนจะไม่ได้รับอนุญาตในแบบที่คุณพยายามจะใช้

นอกจากนี้การอนุมานว่าค่าใด ๆ ที่ไม่ใช่ศูนย์ = จริงและค่าศูนย์ใด ๆ = false นั้นไม่จำเป็นต้องพกพา (ใช่เชื่อหรือไม่ระบบบางระบบปฏิบัติต่อ 0 ว่าเป็นจริงและ 1 เป็นเท็จ ) ดังนั้นภายใต้เป้าหมาย # 2 ด้านบน . คุณยังสามารถส่งแน่นอน


4
นอกจากจะเพิ่มใน Java 8 แล้วจะไม่มีการส่งสัญญาณชัดเจนระหว่างประเภทตัวเลขและ Booleans ในการแปลงค่าตัวเลขให้เป็น Boolean คุณต้องใช้ตัวดำเนินการเปรียบเทียบ เพื่อแปลงค่าบูลีนเป็นตัวเลขที่คุณมักจะใช้ตัวดำเนินการประกอบ
Peter Taylor

คุณสามารถสร้างกรณีที่ไม่อนุญาตให้ใช้การกำหนดค่าของมันเนื่องจากการพิมพ์ผิดเหล่านั้น แต่เมื่อพิจารณาถึงความถี่ที่คุณเห็น == จริงและ == เท็จใน Java การ จำกัด นิพจน์การควบคุมเป็นประเภทบูลีนแบบไม่ต้องป้อนข้อมูลจะช่วยอะไรมาก
Deduplicator

Java จะรับรองความสะดวกในการพกพาไปที่ "บางระบบถือว่า 0 เป็นจริงและ 1 เป็นเท็จ" เนื่องจากเป็นศูนย์ของ Java และ Java ของเท็จ มันไม่สำคัญว่าระบบปฏิบัติการคิดอย่างไรกับมัน
maaartinus

-1

เป็นตัวอย่างสิ่งที่ภาษาอื่น ๆ ทำ: Swift ต้องการนิพจน์ของชนิดที่รองรับโปรโตคอล "BooleanType" ซึ่งหมายความว่าจะต้องมีวิธี "boolValue" ประเภท "บูล" สนับสนุนโปรโตคอลนี้อย่างชัดเจนและคุณสามารถสร้างประเภทของคุณเองที่สนับสนุน ประเภทจำนวนเต็มไม่รองรับโปรโตคอลนี้

ในรุ่นที่เป็นตัวเลือกภาษารุ่นเก่ารองรับ "BooleanType" ดังนั้นคุณสามารถเขียน "if x" แทน "if x! = nil" ซึ่งทำโดยใช้ "ตัวเลือกบูล" สับสนมาก บูลเสริมมีค่าเป็นศูนย์, เท็จหรือจริงและ "ถ้า b" จะไม่ถูกดำเนินการถ้า b เป็นศูนย์และดำเนินการถ้า b เป็นจริงหรือเท็จ สิ่งนี้ไม่ได้รับอนุญาตอีกต่อไป

และดูเหมือนจะมีผู้ชายคนหนึ่งที่ไม่ชอบการเปิดโลกทัศน์ของคุณ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.