เมื่อใดที่จะใช้คลาส vs ดั้งเดิมใน Java?


54

ฉันเห็นว่า Java มีบูลีน (คลาส) และบูลีน (ดั้งเดิม) ในทำนองเดียวกันมีจำนวนเต็ม (คลาส) เทียบกับ int (ดั้งเดิม) อะไรคือวิธีปฏิบัติที่ดีที่สุดในการใช้เวอร์ชันดั้งเดิมกับคลาส? โดยทั่วไปฉันควรใช้รุ่นคลาสอยู่เสมอเว้นแต่ฉันจะมีเหตุผลที่เฉพาะเจาะจง วิธีที่นิยมใช้กันมากที่สุดคืออะไร?



ในใจของฉันคนที่ไม่ได้เรียนพวกเขาเป็นกล่อง พวกมันอยู่ที่นั่นเท่านั้นเพื่อให้คุณสามารถใช้ดั้งเดิมที่ต้องการวัตถุเช่นในคอลเลกชัน คุณไม่สามารถเพิ่มจำนวนเต็มสองตัวได้ (คุณสามารถทำท่านี้ได้ แต่จริงๆแล้ว java คือ auto Boxing | การยกเลิกการกำหนดค่าสำหรับคุณ)
หิน

คำตอบ:


47

ในข้อ 5 ของจาวาที่มีประสิทธิภาพ Joshua Bloch กล่าว

บทเรียนที่มีความชัดเจน: ต้องการพื้นฐานเพื่อ primitives กล่องและดูออก autoboxing

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

แก้ไข: เหตุผลของ Joshua Bloch คือ:

// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
    Long sum = 0L;
    for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
    }
    System.out.println(sum);
}

โปรแกรมนี้ได้คำตอบที่ถูกต้อง แต่ช้ากว่าที่ควรจะเป็นเพราะข้อผิดพลาดในการพิมพ์ตัวอักษรหนึ่งตัว ตัวแปรsumถูกประกาศเป็นLongแทนที่จะเป็น a longซึ่งหมายความว่าโปรแกรมจะสร้างLongอินสแตนซ์ที่ไม่จำเป็นประมาณ 2 ^ 31 (โดยประมาณหนึ่งครั้งสำหรับแต่ละครั้งที่long iเพิ่มเข้าไปLong sum) การเปลี่ยนการประกาศผลรวมจากLongเป็นlongลดเวลาทำงานจาก 43 วินาทีเป็น 6.8 วินาทีบนเครื่องของฉัน


2
มันจะช่วยได้ถ้าคุณระบุเหตุผลของโบลชมากกว่าจะเป็นการสรุปข้อสรุปของเขา!
vaughandroid

@Baqueta ฉันแก้ไขโพสต์แล้ว เหตุผลก็คือประสิทธิภาพ
m3th0dman

มันค่อนข้างชัดเจนกว่านี้ขอบคุณ ฉันรู้สึกเป็นธรรมในการโพสต์คำตอบของฉันเองตอนนี้ :)
vaughandroid

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

ดูเหมือนว่าสิ่งที่ JIT ควรจัดการ
deFreitas

28

การปฏิบัติมาตรฐานคือไปกับสิ่งที่ไม่คาดคิดเว้นแต่คุณจะจัดการกับยาชื่อสามัญ (ตรวจสอบให้แน่ใจว่าคุณได้รับทราบถึงการautoboxing และ unboxing !)

มีหลายเหตุผลที่ควรทำตามการประชุม:

1. คุณหลีกเลี่ยงข้อผิดพลาดง่าย ๆ :

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

ความผิดพลาดที่ commmon มากที่สุดคือการใช้แทนa == b a.equals(b)ผู้คนเคยชินกับการทำสิ่งa == bดั้งเดิมดังนั้นจึงทำได้ง่ายเมื่อคุณใช้ Object wrappers

Integer a = new Integer(2);
Integer b = new Integer(2);
if (a == b) { // Should be a.equals(b)
    // This never gets executed.
}
Integer c = Integer.valueOf(2);
Integer d = Integer.valueOf(2);
if (c == d) { // Should be a.equals(b), but happens to work with these particular values!
    // This will get executed
}
Integer e = 1000;
Integer f = 1000;
if (e == f) { // Should be a.equals(b)
    // Whether this gets executed depends on which compiler you use!
}

2. การอ่าน:

ลองพิจารณาสองตัวอย่างต่อไปนี้ คนส่วนใหญ่จะบอกว่าสองสามารถอ่านได้มากขึ้น

Integer a = 2;
Integer b = 2;
if (!a.equals(b)) {
    // ...
}
int c = 2;
int d = 2;
if (c != d) {
    // ...
}

3. ประสิทธิภาพ:

ความจริงก็คือการใช้ Object wrappers เป็นแบบดั้งเดิมนั้นช้ากว่าการใช้แบบดั้งเดิม คุณกำลังเพิ่มค่าใช้จ่ายของ instantiation วัตถุโทรวิธีการอื่น ๆ เพื่อสิ่งที่คุณใช้ทั่วทุกสถานที่

Knuth's "... พูดถึง 97% ของเวลา: การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด" การอ้างไม่ได้นำมาใช้จริงที่นี่ เขากำลังพูดถึงการเพิ่มประสิทธิภาพซึ่งทำให้รหัส (หรือระบบ) มีความซับซ้อนมากขึ้น - ถ้าคุณเห็นด้วยกับข้อที่ 2 นี่คือการเพิ่มประสิทธิภาพซึ่งทำให้รหัสนั้นซับซ้อนน้อยลง !

4. เป็นการประชุม:

หากคุณเลือกตัวเลือกโวหารที่แตกต่างกันถึง 99% ของโปรแกรมเมอร์ Java คนอื่น ๆ มีอยู่ 2 ข้อเสีย:

  • คุณจะพบว่ารหัสของคนอื่นอ่านยากขึ้น 99% ของตัวอย่าง / บทช่วยสอน / อื่น ๆ ในนั้นจะใช้ระบบพื้นฐาน เมื่อใดก็ตามที่คุณอ่านคุณจะมีค่าใช้จ่ายเกี่ยวกับความรู้ความเข้าใจเป็นพิเศษเกี่ยวกับลักษณะของสไตล์ที่คุณคุ้นเคย
  • คนอื่นจะพบรหัสของคุณอ่านยากขึ้น เมื่อใดก็ตามที่คุณถามคำถามเกี่ยวกับ Stack Overflow คุณจะต้องกรองคำตอบ / ความคิดเห็นที่ถามว่า "ทำไมคุณไม่ใช้ primitives" หากคุณไม่เชื่อฉันเพียงแค่ดูการต่อสู้ที่ผู้คนมีมากกว่าสิ่งต่าง ๆ เช่นการวางตำแหน่งซึ่งไม่มีผลกับรหัสที่สร้างขึ้น!

โดยปกติแล้วฉันจะแสดงรายการเคาน์เตอร์คะแนน แต่ฉันก็ไม่สามารถคิดเหตุผลที่ดีที่จะไม่ไปกับการประชุมที่นี่!


2
อย่า suggets ==คนเพื่อเปรียบเทียบกับวัตถุ equals()วัตถุที่ควรจะนำมาเปรียบเทียบกับ
Tulains Córdova

2
@ user61852 ฉันไม่ได้แนะนำว่าเป็นสิ่งที่ต้องทำ แต่เป็นความผิดพลาดทั่วไปที่เกิดขึ้น! ฉันควรทำให้ชัดเจนขึ้นหรือไม่
vaughandroid

ใช่คุณไม่ได้พูดถึงว่าวัตถุที่เปล่งเสียงถูกนำมาเปรียบเทียบกับequals()... คุณให้วิธีแก้ปัญหาแก่พวกเขาเพื่อให้การเปรียบเทียบวัตถุที่==ให้ผลลัพธ์ที่คาดหวัง
Tulains Córdova

จุดดี. เปลี่ยนแล้ว
vaughandroid

ฉันเพิ่มequals()ข้อมูลโค้ดที่สองและเปลี่ยนการลงคะแนนของฉัน
Tulains Córdova

12

ฉันมักจะไปกับดั้งเดิม อย่างไรก็ตามหนึ่งในลักษณะพิเศษของการใช้คลาสเช่นIntegerและBooleanเป็นไปได้ของการกำหนดให้nullกับตัวแปรเหล่านั้น แน่นอนซึ่งหมายความว่าคุณต้องnullตรวจสอบตลอดเวลา แต่ยังดีกว่าที่จะได้รับ NullPointerException มากกว่าที่จะมีข้อผิดพลาดทางตรรกะเนื่องจากการใช้บางส่วนintหรือbooleanตัวแปรที่ไม่ได้เริ่มต้นอย่างถูกต้อง

แน่นอนตั้งแต่ Java 8 คุณสามารถ (และอาจจะ) ไปอีกขั้นหนึ่งและแทนที่จะเป็นเช่นIntegerคุณสามารถใช้Optional<Integer>สำหรับตัวแปรที่อาจมีหรือไม่มีค่า

นอกจากนี้ยังแนะนำความเป็นไปได้ที่จะใช้nullในการกำหนดตัวแปรเหล่านั้นเป็นค่า " ไม่รู้จัก " หรือ " ตัวแทน " นี้จะมีประโยชน์ในบางสถานการณ์เช่นในลอจิก Ternary หรือคุณอาจต้องการตรวจสอบว่าวัตถุบางอย่างตรงกับแม่แบบบางอย่างหรือไม่ ในกรณีนี้คุณสามารถใช้nullสำหรับตัวแปรเหล่านั้นในแม่แบบที่สามารถมีค่าใด ๆ ในวัตถุ


2
(ไม่ใช่ downvote ของฉัน แต่ ... ) Java ตรวจพบตัวแปรที่ไม่กำหนดค่าเริ่มต้นแล้วและจะไม่อนุญาตให้คุณอ่านตัวแปรจนกว่าทุกเส้นทางของรหัสที่นำไปสู่การใช้งานได้กำหนดค่าไว้อย่างแน่นอน ดังนั้นคุณจะไม่ได้รับมากจากความสามารถในการกำหนดnullเป็นค่าเริ่มต้น ในทางตรงกันข้ามคุณควรจะดีกว่าไม่ใช่ "การเริ่มต้น" ตัวแปรเลย การตั้งค่าเริ่มต้นใด ๆ แม้nullปิดคอมไพเลอร์ ... แต่ยังช่วยให้มันจากการตรวจสอบการขาดการกำหนดที่มีประโยชน์ตามเส้นทางรหัสทั้งหมด ดังนั้นข้อผิดพลาดที่คอมไพเลอร์อาจจับได้ส่งผ่านไปยังรันไทม์
cHao

@cHao แต่ถ้าไม่มีค่าเริ่มต้นที่เหมาะสมในการเริ่มต้นตัวแปรด้วย? คุณสามารถตั้งค่าเป็น0.0หรือหรือ-1หรือInteger.MAX_VALUEหรือFalseแต่ท้ายที่สุดคุณไม่รู้ว่าเป็นค่าเริ่มต้นหรือค่าจริงที่กำหนดให้กับตัวแปรนั้นหรือไม่ ในกรณีที่สิ่งนี้สำคัญการมีnullค่าอาจมีความชัดเจน
tobias_k

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

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

คุณมีกรณีเช่นนี้ในใจหรือไม่? กรณีที่ฉันคิดว่าอยู่ที่ขอบเขตของอินเทอร์เฟซ (เช่น: เป็นพารามิเตอร์หรือชนิดส่งคืน) ... แต่ถึงแม้จะมีก็จะดีกว่าถ้าถูกห่อ (Naked nullมาพร้อมกับโฮสต์ทั้งหมดของปัญหารวมถึงความหวาดระแวง null) ภายในฟังก์ชันตัวแปรที่อาจถูกกำหนดค่าเริ่มต้น ณ จุดใช้งานโดยทั่วไปจะระบุกรณีที่ไม่ถูกเปิด (การวิเคราะห์การมอบหมายที่ชัดเจนนั้นง่ายดังนั้นจึงเป็นไปได้ที่จะเกิดความผิดพลาดได้ แต่คุณสามารถแก้ไขได้โดยการทำให้ตรรกะง่ายขึ้น)
cHao

2

ในคำพูดของคนธรรมดา:

คุณใช้โปรแกรมเสริมเมื่อคุณต้องการเพิ่มสิ่งต่าง ๆ ลงในคอลเลกชัน

คอลเลกชันไม่สามารถเก็บดั้งเดิมได้


0

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

ตามกฎทั่วไปคุณควรพยายามใช้ชนิดข้อมูลดั้งเดิมทุกครั้งที่ทำได้

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