ตรวจสอบสองข้อโต้แย้งใน Java ทั้งไม่เป็นโมฆะหรือทั้งสองอย่างหรูหรา


161

ฉันใช้สปริงบูตเพื่อพัฒนาโครงการเชลล์ที่ใช้ในการส่งอีเมลเช่น

sendmail -from foo@bar.com -password  foobar -subject "hello world"  -to aaa@bbb.com

ถ้าfromและpasswordมีปากเสียงจะหายไปผมใช้ผู้ส่งเริ่มต้นและรหัสผ่านเช่นและnoreply@bar.com123456

ดังนั้นหากผู้ใช้ผ่านการfromโต้แย้งพวกเขาจะต้องผ่านการpasswordโต้แย้งและในทางกลับกัน กล่าวคือไม่ว่าทั้งสองอย่างจะไม่เป็นโมฆะหรือทั้งสองอย่างนั้นเป็นโมฆะ

ฉันจะตรวจสอบสิ่งนี้ได้อย่างหรูหราได้อย่างไร

ตอนนี้ทางของฉันคือ

if ((from != null && password == null) || (from == null && password != null)) {
    throw new RuntimeException("from and password either both exist or both not exist");
}

14
โปรดทราบว่าการใช้ช่องว่างทำให้อ่านโค้ดได้ง่ายขึ้นมากเพียงแค่เพิ่มช่องว่างระหว่างตัวดำเนินการในรหัสปัจจุบันของคุณจะช่วยเพิ่มความสามารถในการอ่านได้อย่างมีนัยสำคัญ
Jon Skeet

8
โปรดกำหนด "ความสง่างาม"
Renaud

คุณต้องมีชุดอาร์กิวเมนต์ที่แยกต่างหากสำหรับข้อมูลรับรองการตรวจสอบสิทธิ์ SMTP และที่อยู่อีเมลของผู้ส่งซองจดหมาย ที่Fromอยู่อีเมลนั้นไม่ใช่ชื่อการรับรองความถูกต้อง SMTP เสมอไป
Kaz

3
มันไม่สามารถปรับให้เหมาะสมได้มากขึ้นจริง ๆ มันเป็นหนึ่งบรรทัดของโค้ดที่อ่านได้และไม่มีสิ่งใดที่จะได้รับจากการปรับให้เหมาะสม
diynevala

3
หมายเหตุด้านข้าง: หากนี่เป็นเชลล์สคริปต์รหัสผ่านจะไม่ถูกบันทึกในประวัติเชลล์หรือไม่
สัมผัสร่างกายของฉัน

คำตอบ:


331

มีวิธีใช้ตัวดำเนินการ^( XOR ):

if (from == null ^ password == null) {
    // Use RuntimeException if you need to
    throw new IllegalArgumentException("message");
}

ifสภาพจะเป็นจริงถ้าเพียงตัวแปรหนึ่งเป็นโมฆะ

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

if ((from == null) && (password != null)) {
    throw new IllegalArgumentException("If from is null, password must be null");
}
if ((from != null) && (password == null)) {
    throw new IllegalArgumentException("If from is not null, password must not be null");
}

สามารถอ่านได้ง่ายขึ้นและเข้าใจได้ง่ายขึ้นและใช้การพิมพ์เพียงเล็กน้อยเท่านั้น


152
มีเหตุผลว่าทำไมแฮคเกอร์กับบูลส์สองคนจึงนิยมใช้!=บูลสองอัน?
Eric Lippert

1
!=ว้าวผมไม่ทราบแฮคเกอร์ในแบบบูลที่เป็นเช่นเดียวกับ Mindblown และนั่นคือจำนวน upvotes ที่สูงมากในความคิดเห็น และเพื่อเพิ่มคุณภาพให้กับความคิดเห็นนี้ใช่ฉันยังคิดว่าการให้ข้อความแสดงข้อผิดพลาดที่แตกต่างกันสำหรับกรณีข้อผิดพลาดที่แตกต่างกันนั้นดีกว่าเนื่องจากผู้ใช้จะรู้วิธีแก้ไขข้อผิดพลาดได้ดีขึ้น
justhalf

1
สำหรับ 2 องค์ประกอบก็โอเค ทำอย่างไรกับองค์ประกอบ> 2
Anushree Acharjee

ฉันต้องการแสดงข้อความแสดงข้อผิดพลาดหากองค์ประกอบใด ๆ เป็น> 0 โดยค่าเริ่มต้นทั้งหมดจะถูกตั้งค่าเป็น 0 หากทั้งหมดเป็น> 0 มันเป็นสถานการณ์ที่ถูกต้อง ทำอย่างไร
Anushree Acharjee

นี่จะทำให้คำถามสัมภาษณ์ที่ดี ฉันสงสัยว่าโปรแกรมเมอร์% รู้อะไรบ้าง แต่ฉันไม่เห็นด้วยว่ามันไม่ชัดเจนและรับประกันว่าแบ่งเป็น 2 ส่วนif- ข้อความในเอกสารข้อยกเว้นเป็นอย่างดี (ฉันแก้ไขคำถาม แต่มันจะไม่ปรากฏจนกว่าจะยอมรับ)
อดัม

286

ดูเหมือนว่าคุณกำลังพยายามตรวจสอบว่าเงื่อนไข "ว่างเปล่า" ของทั้งสองเหมือนกันหรือไม่ คุณสามารถใช้:

if ((from == null) != (password == null))
{
    ...
}

หรือทำให้ชัดเจนยิ่งขึ้นด้วยตัวแปรตัวช่วย:

boolean gotFrom = from != null;
boolean gotPassword = password != null;
if (gotFrom != gotPassword)
{
    ...
}

18
คุณเป็นหนึ่งในกำลังของ SO Heros @Kaz ของฉัน แต่ไม่มีอะไรบอกว่าทั้งสองนี้หรือว่า แต่ไม่ใช่ทั้งสองเหมือนกันเหมือน^ไม่ :-)
David Bullock

6
@DavidBullock: เมื่อพูดถึงบูลีนไม่มีอะไรพูดว่า "อย่างนี้หรืออย่างนั้น แต่ไม่ใช่ทั้งสองอย่าง" เหมือน ... "ไม่เหมือนกันทั้งคู่"; XOR เป็นเพียงชื่ออื่นสำหรับฟังก์ชัน "ไม่เท่ากับ" เหนือ Booleans
Kaz

5
@Kaz มันแค่^บอกว่า "เฮ้ตัวถูกดำเนินการของฉันเป็นบูลีน" ในลักษณะที่!=ไม่เป็นเช่นนั้น (ถึงแม้ว่าผลกระทบนี้จะลดลงอย่างน่าเสียดายเนื่องจากความต้องการตรวจสอบ "ตัวถูกดำเนินการของฉันอาจเป็นตัวเลขและฉันอาจไม่ได้เป็นผู้ดำเนินการเชิงสัมพันธ์ ณ จุดนี้ในเวลา" ซึ่งฉันให้เป็นข้อเสีย) สถานะฮีโร่ของคุณไม่ลดน้อยลงถึงแม้จะไม่เห็นด้วยกับฉัน :-)
David Bullock

3
หลังจากอ่านข้อกำหนดของปัญหาแล้ววิธีแก้ไขของคุณรหัสนั้นสมเหตุสมผลและสามารถอ่านได้ แต่ในฐานะนักพัฒนา 4 ปี (ยังอยู่ในโรงเรียน) การอ่านรหัสนี้แล้วพยายามคิดว่า "ความต้องการทางธุรกิจ" ที่เกิดขึ้นจะเป็นอะไรที่ปวดหัว จากรหัสนี้ฉันไม่เข้าใจทันที " fromและpasswordทั้งคู่ต้องเป็นโมฆะหรือทั้งคู่ต้องไม่เป็นโมฆะ" บางทีนั่นอาจเป็นเพียงฉันและไม่มีประสบการณ์ของฉัน แต่ฉันชอบวิธีการแก้ปัญหาที่อ่านง่ายกว่าในคำตอบของ @ stig-hemmer แม้ว่าจะมีค่าใช้จ่ายอีก 3 บรรทัด ฉันเดาว่าไม่ได้รับ ทันทีbool != bool- มันไม่ง่ายเลย
Chris Cirefice

2
@DavidBullock ตัว^ดำเนินการเป็นตัวดำเนินการระดับบิต มันไม่ได้แปลว่าโอเปอเรเตอร์นั้นเป็นบูลีน ผู้ประกอบการ XOR xorบูลคือ
Brilliand

222

โดยส่วนตัวแล้วฉันชอบที่จะอ่านได้อย่างสง่างาม

if (from != null && password == null) {
    throw new RuntimeException("-from given without -password");
}
if (from == null && password != null) {
    throw new RuntimeException("-password given without -from");
}

52
+1 เพื่อข้อความที่ดีกว่า นี่เป็นสิ่งสำคัญไม่มีใครชอบ handwaving "มีอะไรผิดพลาด" -ข้อความที่เสียหายดังนั้นไม่มีใครควรทำให้เกิดข้อความดังกล่าว อย่างไรก็ตามในทางปฏิบัติเราควรเลือกข้อยกเว้นที่เฉพาะเจาะจงมากกว่านี้ (โดยเฉพาะอย่างยิ่งIllegalArgumentExceptionแทนที่จะเป็นเปลือยเปล่าRuntimeException)
Marco13

6
@ แมทดูเหมือนว่าการวิจารณ์ของคุณจะไม่ได้อยู่ในโค้ด แต่มีเนื้อหาของข้อยกเว้น แต่ฉันคิดว่าข้อดีของคำตอบนี้อยู่ในโครงสร้างของคำสั่ง if ไม่ใช่ในเนื้อหาของข้อความแสดงข้อผิดพลาด นี่คือคำตอบที่ดีที่สุดเพราะมันปรับปรุงการทำงาน; OP สามารถแทนที่สตริงที่พวกเขาต้องการได้อย่างง่ายดายสำหรับข้อความยกเว้น
Dan Henderson

4
@matt ข้อดีคือมีความเป็นไปได้ที่จะแยกแยะว่าสถานะใดไม่ถูกต้องสองสถานะที่เกิดขึ้น ดังนั้นแทนที่จะพูดว่า "คุณทำสิ่งใดสิ่งหนึ่งในสองสิ่งนี้ผิด" คุณสามารถพูดว่า "คุณทำสิ่งนี้" หรือ "คุณทำอย่างนั้น" (จากนั้นให้ทำตามขั้นตอนการแก้ไขหากต้องการ) ความละเอียดอาจเหมือนกันทั้งสองกรณี แต่ก็ไม่ควรพูดว่า "คุณทำสิ่งเหล่านี้ผิด" ที่มา: ในสภาพแวดล้อมที่ฉันไม่สามารถให้ฟังก์ชั่นนี้ได้ฉันได้ตอบคำถามมากมายจากผู้ใช้ที่พอใจกับเงื่อนไขแรกในข้อความแสดงข้อผิดพลาดของฉัน
Dan Henderson

4
@DanHenderson> คุณไม่ควรพูดว่า "คุณทำสิ่งเหล่านี้ผิด" ฉันไม่เห็นด้วย. รหัสผ่าน / ชื่อผู้ใช้จะปลอดภัยกว่าเพียงแค่บอกว่าชื่อผู้ใช้และรหัสผ่านไม่ตรงกัน
แมตต์

2
@DanHenderson จากมุมมองความปลอดภัยอาจเป็นการดีกว่าที่จะไม่แยกความแตกต่างระหว่างกรณีที่ชื่อผู้ใช้อยู่ในไดเรกทอรีหรือไม่เช่นนั้นผู้โจมตีอาจค้นหาชื่อผู้ใช้ที่ถูกต้อง อย่างไรก็ตามการผสม null / ไม่ใช่ null คือ (ในกรณีนี้) เกิดข้อผิดพลาดในการใช้งานเสมอและการแสดงข้อความแสดงข้อผิดพลาดที่ละเอียดมากขึ้นจะไม่รั่วไหลข้อมูลใด ๆ มากกว่าที่ผู้ใช้ระบุไว้ในตอนแรก
siegi

16

ใส่ฟังก์ชันการทำงานนั้นในวิธีการโต้แย้ง 2 ด้วยลายเซ็น:

void assertBothNullOrBothNotNull(Object a, Object b) throws RuntimeException

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


14
ไม่มีที่ว่างสำหรับการบันทึก vs ((from == null) != (password == null))ที่ง่ายต่อการเข้าใจเช่นกัน มีบางอย่างผิดปกติกับวิธีที่ไม่มีประโยชน์
edc65

3
มีหนึ่งบรรทัดสำหรับคำสั่ง if บรรทัดที่สองสำหรับคำสั่ง Throw และบรรทัดที่สามสำหรับวงเล็บปิด: ทั้งหมดถูกแทนที่ด้วยหนึ่งบรรทัด บันทึกอีกหนึ่งบรรทัดหากคุณให้วงเล็บปิดบรรทัดใหม่!
Traubenfuchs

10
เมื่ออ่านรหัสคุณมีชื่อวิธีการหนึ่งที่คุณต้องเข้าใจเงื่อนไขการเล่นปาหี่
Traubenfuchs

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

1
คุณจะพบปัญหาที่ไม่จำเป็นหากคุณต้องการให้มีข้อความยกเว้นที่เป็นคำอธิบายที่นี่
Honza Brabec

11

โซลูชัน Java 8 จะใช้Objects.isNull(Object)โดยสมมติว่ามีการนำเข้าแบบคงที่:

if (isNull(from) != isNull(password)) {
    throw ...;
}

สำหรับ Java <8 (หรือถ้าคุณไม่ชอบใช้Objects.isNull()) คุณสามารถเขียนisNull()วิธีการของคุณเองได้อย่างง่ายดาย


6
ไม่ชอบ from == null != password == nullเก็บทุกอย่างไว้ในเฟรมเดียวกันของสแต็ก แต่ใช้การObjects.isNull(Object)กดและป๊อปสองเฟรมโดยไม่จำเป็น Objects.isNull (Object) มีอยู่เนื่องจาก 'วิธีนี้มีอยู่เพื่อใช้เป็นเพรดิเคต' (เช่นในสตรีม)
David Bullock

5
วิธีง่าย ๆ เช่นนี้โดยทั่วไปจะถูกแทรกโดย JIT อย่างรวดเร็วดังนั้นผลกระทบต่อประสิทธิภาพอาจไม่ได้รับผลกระทบมากที่สุด เราสามารถถกเถียงถึงการใช้งานของObjects.isNull()- คุณสามารถเขียนของคุณเองถ้าคุณต้องการ - แต่เท่าที่อ่านได้เกี่ยวข้องฉันคิดว่าการใช้isNull()ดีกว่า ยิ่งกว่านั้นคุณต้องการวงเล็บเพิ่มเติมเพื่อทำให้การคอมไพล์นิพจน์ง่าย ๆ : from == null != (password == null).
Didier L

2
ฉันเห็นด้วยกับ JIT'ing (และคำสั่งของการดำเนินการ ... ฉันขี้เกียจ) ยังคง(val == null)เป็นสิ่งอำนวยความสะดวกให้มากเพื่อเปรียบเทียบกับ null ฉันพบว่ามันยากที่จะได้รับมากกว่าสองวิธีไขมันใหญ่ invocations จ้องมองหน้าฉันแม้ว่าวิธีการทำงานค่อนข้าง in-lineable และดี - ชื่อ นั่นเป็นเพียงฉันแม้ว่า ฉันได้ตัดสินใจเมื่อเร็ว ๆ นี้ว่าฉันเป็นคนที่ไม่รุนแรง
David Bullock

4
ใครที่สนใจเฟรมกองซ้อนเดียว สแต็คจะเป็นปัญหาเฉพาะเมื่อคุณจัดการกับการเรียกซ้ำ (อาจไม่มีที่สิ้นสุด) หรือหากคุณมีแอปพลิเคชัน 1,000 ระดับที่มีกราฟวัตถุขนาดเท่ากับทะเลทรายซาฮาร่า
sara

9

นี่เป็นวิธีทั่วไปสำหรับการตรวจสอบจำนวนโมฆะใด ๆ

public static int nulls(Object... objs)
{
    int n = 0;
    for(Object obj : objs) if(obj == null) n++;
    return n;
}

public static void main (String[] args) throws java.lang.Exception
{
    String a = null;
    String b = "";
    String c = "Test";

    System.out.println (" "+nulls(a,b,c));
}

การใช้ประโยชน์

// equivalent to (a==null & !(b==null|c==null) | .. | c==null & !(a==null|b==null))
if (nulls(a,b,c) == 1) { .. }

// equivalent to (a==null | b==null | c==null)
if (nulls(a,b,c) >= 1) { .. }

// equivalent to (a!=null | b!=null | c!=null)
if (nulls(a,b,c) < 3) { .. }

// equivalent to (a==null & b==null & c==null)
if (nulls(a,b,c) == 3) { .. }

// equivalent to (a!=null & b!=null & c!=null)
if (nulls(a,b,c) == 0) { .. }

2
วิธีการที่ดี แต่ครั้งแรกของคุณ "เทียบเท่ากับ" ความคิดเห็นเป็นสิ่งที่ผิดอย่างน่ากลัว (นอกเหนือจากความผิดพลาดของการสะกดคำที่มีอยู่ในทุกความเห็น)
เบนยต์

@BenVoigt ขอขอบคุณสำหรับการแจ้งเตือนแก้ไขตอนนี้
Khaled.K

9

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

// use defaults if neither is provided
if ((from == null) && (password == null)) {
    from = DEFAULT_SENDER;
    password = DEFAULT_PASSWORD;
}

// we should have a sender and a password now
if (from == null) {
    throw new MissingSenderException();
}
if (password == null) {
    throw new MissingPasswordException();
}

สิทธิประโยชน์เพิ่มเติมคือควรเริ่มต้นอย่างใดอย่างหนึ่งของคุณเป็นโมฆะที่จะตรวจพบเช่นกัน


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


1
ลงเนื่องจากฉันเลือกแบบสุ่มสำหรับสุ่มตัวอย่างหนึ่งในคำตอบของคุณและไม่สามารถค้นหาลิงก์ xkcd ที่สัญญาไว้! อัปยศคุณ!
dhein

@Zaibis ในการป้องกันของฉันมันเป็นแค่งานอดิเรกไม่ใช่งานประจำ แต่ฉันจะดูว่าฉันสามารถหา ...
SQB

8

ฉันอยากจะแนะนำทางเลือกอื่นซึ่งเป็นวิธีที่ฉันจะเขียนโค้ดนี้:

if( from != null )
{
    if( password == null )
        error( "password required for " + from );
}
else
{
    if( password != null )
        warn( "the given password will not be used" );
}

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

นอกจากนั้นมันจะทำการเปรียบเทียบจำนวนขั้นต่ำเท่านั้นดังนั้นจึงไม่แพงกว่าทางเลือกที่"หรูหรา"มากกว่า แม้ว่าประสิทธิภาพจะไม่เป็นปัญหาที่นี่มากนักเนื่องจากการเริ่มต้นกระบวนการใหม่มีราคาแพงกว่าการตรวจสอบ null เป็นพิเศษอยู่แล้ว


ฉันแสดงความคิดเห็นในเชิงสนทนาดังนั้นฉันอาจติดตามด้วย "// เรามีโมฆะ" ความกะทัดรัดของตัวอย่างทำให้สิ่งนี้น้อยมาก ฉันชอบความชัดเจนของตรรกะและการลดการทดสอบที่นำเสนอนี้
เนท

มันใช้งานได้ดีอย่างสมบูรณ์ แต่การทำรังหากข้อความไม่ชัดเจนและรกมากสำหรับฉัน นั่นเป็นเพียงความเห็นส่วนตัวดังนั้นฉันดีใจที่คำตอบนี้อยู่ที่นี่เป็นตัวเลือกอื่น
Kevin Wells

เท่าที่ความสง่างามไปนี่อาจเป็นหนึ่งในวิธีที่สง่างามน้อยที่สุดในการทำเช่นนี้
dramzy

7

ฉันคิดว่าวิธีที่ถูกต้องในการจัดการสิ่งนี้คือการพิจารณาสถานการณ์ที่สาม: ทั้ง 'จาก' และ 'รหัสผ่าน' ถูกจัดเตรียมไว้ไม่มีการจัดหาทั้งสองอย่าง

if(from != null && password != null){
    //use the provided values
} else if(from == null && password == null){
    //both values are null use the default values
} else{
   //throw an exception because the input is not correct.
}

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

throw new IllegalArgumentException("form of " + form + 
    " cannot be used with a "
    + (password==null?"null":"not null") +  
    " password. Either provide a value for both, or no value for both"
);

2
รหัสนี้ไม่ถูกต้องเพราะมันใช้งานไม่ได้ แต่เป็นเพราะเข้าใจยาก การดีบักโค้ดประเภทนั้นที่เขียนโดยคนอื่นเป็นแค่ฝันร้าย
NO_NAME

2
@NO_NAME ฉันไม่เข้าใจว่าทำไมจึงเข้าใจยาก OP จัดให้สามกรณี: เมื่อมีการให้ทั้งแบบฟอร์มและรหัสผ่านเมื่อไม่ได้จัดเตรียมไว้ให้และทั้งสองกรณีจะมีข้อยกเว้น คุณอ้างถึงเงื่อนไขตรงกลางเพื่อตรวจสอบว่าทั้งสองเป็นโมฆะหรือไม่?
แมตต์

2
ฉันเห็นด้วยกับ @NO_NAME ตัวพิมพ์กลางนั้นใช้การแยกวิเคราะห์มากเกินไป ยกเว้นว่าคุณแยกวิเคราะห์บรรทัดบนสุดคุณจะไม่ได้รับค่าว่างใด ๆ ที่เกี่ยวข้องกับตัวกลาง ความเท่าเทียมกันของจากและรหัสผ่านในกรณีนั้นเป็นเพียงผลข้างเคียงของการไม่เป็นโมฆะ
jimm101

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

2
@ แมทอาจไม่มีประโยชน์ด้านประสิทธิภาพ - มันไม่ชัดเจนว่าคอมไพเลอร์จะทำอะไรและชิปจะดึงจากหน่วยความจำในกรณีนี้ วงจรโปรแกรมเมอร์จำนวนมากสามารถเขียนลงบนการปรับให้เหมาะสมซึ่งไม่ได้ผลอะไรเลย
jimm101

6

นี่เป็นวิธีที่ค่อนข้างตรงไปตรงมาซึ่งไม่เกี่ยวข้องกับ Xor og ใด ๆ ที่มีความยาว อย่างไรก็ตามคุณต้องใช้ verbose มากกว่านี้เล็กน้อย แต่ในทางกลับกันคุณสามารถใช้ข้อยกเว้นแบบกำหนดเองที่ฉันแนะนำเพื่อรับข้อความแสดงข้อผิดพลาดที่มีความหมายมากขึ้น

private void validatePasswordExists(Parameters params) {
   if (!params.hasKey("password")){
      throw new PasswordMissingException("Password missing");
   }
}

private void validateFromExists(Parameters params) {
   if (!params.hasKey("from")){
      throw new FromEmailMissingException("From-email missing");
   }
}

private void validateParams(Parameters params) {

  if (params.hasKey("from") || params.hasKey("password")){
     validateFromExists(params);
     validatePasswordExists(params);
  }
}

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

1
@ anphu ไม่เพียงแค่ไม่มี การใช้ข้อยกเว้นจะเพิ่มความสามารถในการอ่านโค้ดเนื่องจากจะเป็นการกำจัดส่วนคำสั่ง if-else และทำให้การไหลของรหัสชัดเจนขึ้น docs.oracle.com/javase/tutorial/essential/exceptions/ …
Arnab Datta

1
ฉันคิดว่าคุณกำลังสับสนสิ่งที่ยอดเยี่ยมอย่างแท้จริงเทียบกับสิ่งที่เกิดขึ้นเป็นประจำและอาจถือเป็นส่วนหนึ่งของการดำเนินการตามปกติ java doc ใช้ตัวอย่างที่ไม่ได้อยู่ในหน่วยความจำในขณะที่อ่านไฟล์ การพบพารามิเตอร์ที่ไม่ถูกต้องนั้นไม่ได้ยอดเยี่ยม "อย่าใช้ข้อยกเว้นสำหรับการควบคุมการไหลปกติ" - blogs.msdn.com/b/kcwalina/archive/2005/03/16/396787.aspx
an phu

ก่อนอื่น: หากไม่มีข้อโต้แย้งที่เหมาะสม / ไม่เหมาะสมทำไม IllegalArgumentException ยังคงมีอยู่? ข้อที่สอง: หากคุณเชื่ออย่างแท้จริงว่าการขัดแย้งที่ไม่ถูกต้องนั้นไม่ได้รับการยกเว้นก็ไม่ควรมีการตรวจสอบความถูกต้อง กล่าวอีกนัยหนึ่งเพียงลองทำสิ่งที่เกิดขึ้นและเมื่อมีสิ่งผิดปกติเกิดขึ้นให้ทำการยกเว้น วิธีนี้ใช้ได้ แต่การลงโทษที่คุณพูดถึงเกิดขึ้นที่นี่ไม่ใช่วิธีที่ฉันแนะนำ ข้อยกเว้น Java จะไม่ช้าเมื่อโยน; มันเป็น stacktrace ที่ใช้เวลาในการกู้คืน
Arnab Datta

บริบทเป็นกุญแจสำคัญ ในบริบทของ OP (แอพ sendmail) การลืมชื่อผู้ใช้และรหัสผ่านเป็นเรื่องปกติและคาดหวัง ในจรวดนำวิถีอัลโกพิกัดว่างควรทิ้งข้อยกเว้น ฉันไม่ได้บอกว่าไม่ได้ตรวจสอบพารามิเตอร์ ในบริบทของ OP ฉันบอกว่าอย่าใช้ข้อยกเว้นเพื่อขับเคลื่อนตรรกะของแอปพลิเคชัน การคลี่คลายสแต็คเทรซเป็นการตีที่สมบูรณ์แบบที่ฉันกำลังพูดถึง การใช้วิธีการของคุณในที่สุดคุณก็จะได้รับ PasswordMissingException / FromEmailMissingException เช่นผ่อนคลายหรือแย่ลงปล่อยให้มันจัดการไม่ได้ .
ภู่

6

ดูเหมือนว่าไม่มีใครพูดถึงผู้ประกอบการที่ประกอบไปด้วย :

if (a==null? b!=null:b==null)

ทำงานได้ดีสำหรับการตรวจสอบเงื่อนไขนี้โดยเฉพาะ แต่ไม่สามารถสรุปตัวแปรสองตัวที่ผ่านมาได้ดี


1
หน้าตาดี แต่ยากที่จะเข้าใจกว่า^, !=มีสอง bools หรือสองifs
coolguy

@coolguy ฉันเดาว่าผู้ประกอบการที่ประกอบไปด้วยกันเป็นเรื่องธรรมดามากกว่าผู้ดำเนินการ XOR (ไม่ต้องพูดถึง 'ความประหลาดใจจิต' ทุกครั้งที่คุณเห็น XOR ในรหัสที่ไม่ได้ทำการบิต) และสูตรนี้มีแนวโน้มที่จะหลีกเลี่ยงการตัด และวางข้อผิดพลาดที่ทำให้เกิดปัญหาสองครั้งหาก
gbronner

ไม่แนะนำ. สิ่งนี้จะไม่แตกต่างกันระหว่าง (a! = null && b == null) และ (a == null && b! = null) หากคุณกำลังจะใช้ผู้ประกอบการที่ประกอบไปด้วย:a == null ? (b == null? "both null" : "a null while b is not") : (b ==null? "b null while a is not")
Arnab Datta

5

ตามที่ฉันเห็นความตั้งใจของคุณไม่จำเป็นต้องตรวจสอบทั้งค่าพิเศษเฉพาะเสมอ แต่เพื่อตรวจสอบว่าpasswordเป็นโมฆะถ้าและเฉพาะในกรณีที่fromไม่เป็นโมฆะ คุณสามารถละเว้นpasswordอาร์กิวเมนต์ที่กำหนดและใช้ค่าเริ่มต้นของคุณเองหากfromเป็นโมฆะ

เขียนในหลอกต้องเป็นเช่นนี้:

if (from == null) { // form is null, ignore given password here
    // use your own defaults
} else if (password == null) { // form is given but password is not
    // throw exception
} else { // both arguments are given
    // use given arguments
}

4
ปัญหาเดียวของเรื่องนี้ก็คือเมื่อผู้ใช้ส่งpasswordโดยไม่ต้องfromระบุพวกเขาอาจตั้งใจจะแทนที่รหัสผ่าน แต่เก็บบัญชีเริ่มต้น หากผู้ใช้ทำเช่นนั้นมันเป็นคำสั่งที่ไม่ถูกต้องและควรแจ้งให้ทราบ โปรแกรมไม่ควรดำเนินการต่อราวกับอินพุตถูกต้องและดำเนินการต่อและลองใช้บัญชีเริ่มต้นด้วยรหัสผ่านที่ผู้ใช้ไม่ได้ระบุ ฉันสาบานกับโปรแกรมเช่นนั้น
David Bullock

4

ฉันไม่แปลกใจเลยที่ไม่มีใครพูดถึงวิธีแก้ปัญหาอย่างง่ายในการทำfromและpasswordทุ่งนาของชั้นเรียนและผ่านการอ้างอิงถึงตัวอย่างของชั้นเรียนนั้น:

class Account {
    final String name, password;
    Account(String name, String password) {
        this.name = Objects.requireNonNull(name, "name");
        this.password = Objects.requireNonNull(password, "password");
    }
}

// the code that requires an account
Account from;
// do stuff

ที่นี่ fromอาจเป็นโมฆะหรือไม่เป็นโมฆะและถ้ามันไม่เป็นโมฆะทั้งสองฟิลด์มีค่าไม่เป็นโมฆะ

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

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


ไม่ทำงานอย่างที่คาดไว้เนื่องจากnew Account(null, null)จะส่ง NPE แม้ว่าบัญชีที่มีชื่อ null และรหัสผ่านยังคงใช้ได้
HieuHT

แนวคิดคือการส่งผ่านค่าว่างหากชื่อและรหัสผ่านเป็นโมฆะไม่ว่าในโลกแห่งความจริงคุณจะบอกว่ามีบัญชีอยู่หรือไม่
Reinstate Monica

สิ่งที่ฉันได้รับจาก OP คือ กล่าวได้ว่าทั้งคู่ไม่เป็นโมฆะหรือทั้งคู่เป็นโมฆะ ฉันแค่สงสัยว่าคุณสามารถสร้างวัตถุบัญชีที่มีทั้งชื่อและรหัสผ่านเป็นโมฆะได้อย่างไร
HieuHT

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