เมื่อใดที่คุณใช้ varargs ใน Java


192

ฉันกลัว varargs ฉันไม่รู้ว่าจะใช้มันเพื่ออะไร

นอกจากนี้ยังรู้สึกอันตรายที่จะให้ผู้คนผ่านการโต้แย้งได้มากเท่าที่ต้องการ

ตัวอย่างของบริบทที่จะเป็นสถานที่ที่ดีในการใช้เป็นอย่างไร


3
ฉันไม่เห็นว่าทำไม "อันตราย" มันไม่อันตรายกว่าการมีวิธีที่เรียกว่าจำนวนครั้งมากที่มีข้อโต้แย้งที่แตกต่างกัน คุณมีความกังวลกับสแต็กหรือไม่? จากนั้นคุณไม่ควรเพราะ varargs ถูกแมปไปยังอาร์เรย์ที่ถูกส่งผ่านโดยการอ้างอิง
jfpoilpret

66
แค่อยากจะพูดสอด: ไม่มีอะไรผิดปกติในการหลีกเลี่ยงคุณสมบัติทางภาษาที่คุณไม่ได้รู้สึกสบายใจ ดีกว่าการใช้ฟีเจอร์ที่คุณไม่เข้าใจ! ;)
Steven

2
เป็นเรื่องปกติที่จะกลัวความไม่รู้จัก ต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการอ่าน varargs นี่docs.oracle.com/javase/tutorial/java/javaOO/arguments.html คุณจะเห็นว่า varargs นั้นไม่น่ากลัวเลย Varargs นั้นมีประโยชน์ การรู้วิธีใช้ varargs ช่วยให้คุณสามารถเขียนวิธีเช่น [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ... )" ) :)
พัฒนา Marius Žilėnas

1
นี่ไม่ใช่คำวิจารณ์ของ varargs แต่จริงๆแล้วมีเหตุผลบางอย่างที่ "กลัว" (จนกว่าคุณจะเข้าใจข้อ จำกัด ของ varargs) นี่คือสาเหตุที่คำอธิบายประกอบ @SafeVarargs มีอยู่ stackoverflow.com/a/14252221/1593924
Jon Coombs

คำตอบ:


150

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

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);

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

79

กฎง่ายๆคือ:

"ใช้ varargs สำหรับวิธีการใด ๆ (หรือตัวสร้าง) ที่ต้องการอาเรย์ของ T (ชนิดใดก็ได้ T อาจเป็น) เป็นอินพุต"

ที่จะทำให้การเรียกใช้วิธีการเหล่านี้ง่ายขึ้น (ไม่จำเป็นต้องทำnew T[]{...})

คุณสามารถขยายกฎนี้เพื่อรวมวิธีการที่มีList<T>อาร์กิวเมนต์โดยมีข้อโต้แย้งว่ามีไว้สำหรับอินพุตเท่านั้น (เช่นรายการจะไม่ได้รับการแก้ไขโดยวิธีการ)

นอกจากนี้ฉันจะละเว้นจากการใช้งานf(Object... args)เพราะพลาดไปสู่วิธีการเขียนโปรแกรมด้วย API ที่ไม่ชัดเจน

ในแง่ของตัวอย่างฉันได้ใช้มันในDesignGridLayoutซึ่งฉันสามารถเพิ่มหลายJComponents ในการโทรครั้งเดียว:

layout.row().grid(new JLabel("Label")).add(field1, field2, field3);

ในโค้ดข้างต้นเพิ่มวิธี () add(JComponent... components)ถูกกำหนดให้เป็น

ในที่สุดการดำเนินการของวิธีการดังกล่าวจะต้องดูแลความจริงที่ว่ามันอาจจะถูกเรียกด้วย vararg ว่าง! หากคุณต้องการกำหนดอย่างน้อยหนึ่งอาร์กิวเมนต์คุณต้องใช้กลอุบายที่น่าเกลียดเช่น:

void f(T arg1, T... args) {...}

ฉันพิจารณาเคล็ดลับนี้น่าเกลียดเพราะการใช้วิธีการจะตรงไปตรงมาน้อยกว่ามีเพียงT... argsในรายการอาร์กิวเมนต์ของ

ความหวังนี้ช่วยชี้แจงจุดเกี่ยวกับ varargs


1
คุณพิจารณาเพิ่มการตรวจสอบสิ่งที่จำเป็นก่อนif (args.length == 0) throw new RuntimeException("foo");หรือไม่ (เนื่องจากผู้โทรละเมิดสัญญา)
Micha Wiedenmann

24
วัตถุประสงค์ของ API ที่ดีก็คือเพื่อป้องกันการใช้ผิดวิธีให้เร็วที่สุดเท่าที่จะเป็นไปได้ดังนั้นเมื่อถึงเวลารวบรวมจึงเป็นไปได้ดังนั้นคำแนะนำสำหรับการทำvoid f(T arg1, T... args)เช่นนี้จะทำให้แน่ใจได้ว่ามันจะไม่ถูกเรียกโดยไม่มีข้อโต้แย้งใด ๆ
jfpoilpret

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

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

34

ฉันใช้ varargs บ่อยครั้งเพื่อส่งออกไปยังล็อกเพื่อการดีบั๊ก

ค่อนข้างทุกคลาสในแอพของฉันมีเมธอด debugPrint ():

private void debugPrint(Object... msg) {
    for (Object item : msg) System.out.print(item);
    System.out.println();
}

จากนั้นภายในวิธีการเรียนฉันมีการโทรดังต่อไปนี้:

debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
    serialNo, ", the grade is ", grade);

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

แน่นอนฉันสามารถหลีกเลี่ยง varargs ได้อย่างง่ายดายและทำสิ่งต่อไปนี้แทน:

private void debugPrint(String msg) {
    System.out.println(msg);
}

debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
    + serialNo + ", the grade is " + grade);

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


4
คุณสามารถใช้วิธีการแก้ปัญหาในซูเปอร์คลาสเพื่อพิมพ์วัตถุใด ๆ โดยใช้การสะท้อน
Lluis Martinez

12

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

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

นอกจากนี้เมื่อชนิดของพารามิเตอร์แตกต่างกันไปจากนั้นการใช้ "การทดสอบ Object ... " จะทำให้โค้ดง่ายขึ้นมาก

ตัวอย่างเช่น:

public int calculate(int...list) {
    int sum = 0;
    for (int item : list) {
        sum += item;
    }
    return sum;
}

อาเรย์ของ int ชนิด (รายการ) จะถูกส่งผ่านทางอ้อมเป็นพารามิเตอร์และถือเป็นอาเรย์ในรหัส

เพื่อความเข้าใจที่ดีขึ้นไปตามลิงค์นี้ (มันช่วยฉันได้มากในการทำความเข้าใจแนวคิดนี้อย่างชัดเจน): http://www.javadb.com/using-varargs-in-java

PS: แม้ฉันกลัวที่จะใช้ varargs เมื่อฉันไม่ได้รู้เรื่องมัน แต่ตอนนี้ฉันชินแล้ว ตามที่มีการกล่าวไว้ว่า: "เรายึดมั่นในสิ่งที่รู้กลัวความไม่รู้จัก" ดังนั้นใช้มันให้มากที่สุดและคุณก็จะเริ่มชอบมัน :)


4
C # เทียบเท่า varargs คือ "params" นอกจากนี้ยังทำสิ่งเดียวกันและยอมรับจำนวนตัวแปร ดูสิ่งนี้เพื่อความเข้าใจที่ดีขึ้น: dotnetperls.com/params
Sarvan

11

Varargs เป็นคุณสมบัติที่เพิ่มเข้ามาใน java เวอร์ชั่น 1.5

ทำไมต้องใช้สิ่งนี้?

  1. เกิดอะไรขึ้นถ้าคุณไม่ทราบจำนวนข้อโต้แย้งที่จะผ่านสำหรับวิธีการ?
  2. ถ้าคุณต้องการส่งจำนวนอาร์กิวเมนต์ที่ไม่ จำกัด ไปยังเมธอด?

มันทำงานอย่างไร

มันสร้างอาร์เรย์ที่มีข้อโต้แย้งที่กำหนดและส่งผ่านอาร์เรย์ไปยังวิธีการ

ตัวอย่าง:

public class Solution {



    public static void main(String[] args) {
        add(5,7);
        add(5,7,9);
    }

    public static void add(int... s){
        System.out.println(s.length);
        int sum=0;
        for(int num:s)
            sum=sum+num;
        System.out.println("sum is "+sum );
    }

}

ผลผลิต:

2

ผลรวมคือ 12

3

ผลรวมคือ 21


6

ฉันมีความกลัวที่เกี่ยวข้องกับ varargs เช่นกัน:

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

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

 Object[] args = new Object[] { 1, 2, 3} ;

 varArgMethod(args);  // not varArgMethod(1,2,3);

 args[2] = "something else";  // this could have unexpected side-effects

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


1
ถูกต้อง แต่ตัวอย่างนี้ดูค่อนข้างไกล เป็นไปได้ไหมที่คนจะใช้ API ของคุณด้วยวิธีนี้?
jfpoilpret

2
คุณไม่มีทางรู้ว่า ... และโดยเฉพาะอย่างยิ่งเมื่อจำนวนของการขัดแย้งไม่ได้ยากในด้านการโทร แต่ยังเก็บรวบรวมพูดในรายการในวงการผ่านในอาร์เรย์ไม่ได้ทั้งหมดที่ผิดปกติ
Thilo

5
ฉันคิดว่าตัวอย่างการใช้งานของคุณคือพูดโดยทั่วไปไม่น่าเป็นไปได้ บางคนอาจโต้แย้งว่า API ของคุณไม่ควรใช้อาร์เรย์อินพุตด้วยวิธีนี้และถ้าเป็นเช่นนั้นก็จะต้องบันทึกไว้
Lawrence Dol

โอเวอร์โหลดวิธีการเพื่อสนับสนุนทั้งสอง; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman

2
@thoolman: ฉันไม่คิดว่าเป็นไปได้ มันจะถือเป็นวิธีการที่ซ้ำกัน
Thilo

1

ฉันใช้ varargs บ่อยครั้งสำหรับตัวสร้างที่สามารถใช้วัตถุตัวกรองบางประเภท ตัวอย่างเช่นส่วนใหญ่ของระบบของเราที่อิงตาม Hadoop ขึ้นอยู่กับ Mapper ที่จัดการการเรียงลำดับและการดีซีเรียลไลเซชันของรายการให้กับ JSON และใช้ตัวประมวลผลจำนวนหนึ่งที่แต่ละรายการใช้เนื้อหาและปรับเปลี่ยนและส่งคืนหรือคืน null ปฏิเสธ


1

ใน Java doc ของ Var-Args มันค่อนข้างชัดเจนว่าการใช้ var args:

http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html

เกี่ยวกับการใช้งานมันบอกว่า:

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

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