พารามิเตอร์เสริม Java


812

ฉันจะใช้พารามิเตอร์ทางเลือกใน Java ได้อย่างไร ข้อมูลจำเพาะอะไรรองรับพารามิเตอร์ทางเลือก

คำตอบ:


516

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

private boolean defaultOptionalFlagValue = true;

public void doSomething(boolean optionalFlag) {
    ...
}

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}

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

การเพิ่มตัวแปรส่วนกลางอาจทำให้เกิดปัญหาอื่น ๆ
Helen Cui

1652

มีหลายวิธีในการจำลองพารามิเตอร์ทางเลือกใน Java:

  1. วิธีการมากไป

    void foo(String a, Integer b) {
        //...
    }
    
    void foo(String a) {
        foo(a, 0); // here, 0 is a default value for b
    }
    
    foo("a", 2);
    foo("a");

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

  2. Varargs

    a) พารามิเตอร์เสริมทั้งหมดเป็นประเภทเดียวกัน:

    void foo(String a, Integer... b) {
        Integer b1 = b.length > 0 ? b[0] : 0;
        Integer b2 = b.length > 1 ? b[1] : 0;
        //...
    }
    
    foo("a");
    foo("a", 1, 2);

    b) ประเภทของพารามิเตอร์ทางเลือกอาจแตกต่างกัน:

    void foo(String a, Object... b) {
        Integer b1 = 0;
        String b2 = "";
        if (b.length > 0) {
          if (!(b[0] instanceof Integer)) { 
              throw new IllegalArgumentException("...");
          }
          b1 = (Integer)b[0];
        }
        if (b.length > 1) {
            if (!(b[1] instanceof String)) { 
                throw new IllegalArgumentException("...");
            }
            b2 = (String)b[1];
            //...
        }
        //...
    }
    
    foo("a");
    foo("a", 1);
    foo("a", 1, "b2");

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

  3. nulls ในการจัดการข้อ จำกัด ของวิธีการก่อนหน้านี้คุณสามารถอนุญาตให้มีค่า Null และวิเคราะห์แต่ละพารามิเตอร์ในเมธอดเนื้อหา:

    void foo(String a, Integer b, Integer c) {
        b = b != null ? b : 0;
        c = c != null ? c : 0;
        //...
    }
    
    foo("a", null, 2);

    ตอนนี้ต้องระบุค่าอาร์กิวเมนต์ทั้งหมด แต่ค่าเริ่มต้นอาจเป็นโมฆะ

  4. คลาสตัวเลือก วิธีนี้คล้ายกับ nulls แต่ใช้ Java 8 เผื่อเลือกสำหรับพารามิเตอร์ที่มีค่าเริ่มต้น:

    void foo(String a, Optional<Integer> bOpt) {
        Integer b = bOpt.isPresent() ? bOpt.get() : 0;
        //...
    }
    
    foo("a", Optional.of(2));
    foo("a", Optional.<Integer>absent());

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

    อัปเดต: Java 8 รวมคลาสjava.util.Optionalเอาต์เอาไว้แล้วดังนั้นจึงไม่จำเป็นต้องใช้ฝรั่งเนื่องจากเหตุผลนี้ใน Java 8 ชื่อวิธีการนั้นแตกต่างกันเล็กน้อย

  5. รูปแบบการสร้าง รูปแบบตัวสร้างถูกใช้สำหรับตัวสร้างและนำไปใช้โดยแนะนำตัวสร้างคลาสแยก:

     class Foo {
         private final String a; 
         private final Integer b;
    
         Foo(String a, Integer b) {
           this.a = a;
           this.b = b;
         }
    
         //...
     }
    
     class FooBuilder {
       private String a = ""; 
       private Integer b = 0;
    
       FooBuilder setA(String a) {
         this.a = a;
         return this;
       }
    
       FooBuilder setB(Integer b) {
         this.b = b;
         return this;
       }
    
       Foo build() {
         return new Foo(a, b);
       }
     }
    
     Foo foo = new FooBuilder().setA("a").build();
  6. แผนที่ เมื่อจำนวนพารามิเตอร์มีขนาดใหญ่เกินไปและโดยปกติแล้วจะใช้ค่าเริ่มต้นเป็นส่วนใหญ่คุณสามารถส่งอาร์กิวเมนต์เมธอดเป็นแผนที่ของชื่อ / ค่าของมัน:

    void foo(Map<String, Object> parameters) {
        String a = ""; 
        Integer b = 0;
        if (parameters.containsKey("a")) { 
            if (!(parameters.get("a") instanceof Integer)) { 
                throw new IllegalArgumentException("...");
            }
            a = (Integer)parameters.get("a");
        }
        if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 

    ใน Java 9 วิธีนี้ง่ายขึ้น:

        @SuppressWarnings("unchecked")
        static <T> T getParm(Map<String, Object> map, String key, T defaultValue)
        {
            return (map.containsKey(key)) ? (T) map.get(key) : defaultValue;
        }
    
        void foo(Map<String, Object> parameters) {
            String a = getParm(parameters, "a", "");
            int b = getParm(parameters, "b", 0);
            // d = ...
        }
    
        foo(Map.of("a","a",  "b",2,  "d","value"));

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


2
@Vitalii Fedorenko อืมฉันคิดว่าคุณทำผิดพลาดการคัดลอกวางเล็กน้อยใน # 6: คุณกำลังตรวจสอบชนิดของ Integer แม้ว่าaตัวแปรของคุณจะเป็นสตริง (cast ถูกต้อง)
ไม่ทราบรหัส

5
@Aetos ลิงก์ของคุณเสียชีวิตแล้ว
Robino

2
@Robino ลิงค์เถียงว่าเป็นตัวเลือกที่ไม่ควรนำมาใช้เป็นพารามิเตอร์ที่นี่ แม้จะใช้Optionalเป็นพารามิเตอร์ในหนึ่งในตัวเลือก แต่คำตอบนี้ดีมาก
Dherik

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

1
นี่น่าจะเป็นคำตอบที่ดีที่สุด - ครอบคลุมทั้งหมด
xproph

104

มีพารามิเตอร์ทางเลือกพร้อม Java 5.0 เพียงประกาศฟังก์ชั่นของคุณเช่นนี้:

public void doSomething(boolean... optionalFlag) {
    //default to "false"
    //boolean flag = (optionalFlag.length >= 1) ? optionalFlag[0] : false;
}

คุณสามารถโทรหาด้วยdoSomething();หรือdoSomething(true);ตอนนี้


13
นี่เป็นคำตอบที่ถูกต้องจริงๆ มันง่ายและกะทัดรัด เพียงจำไว้ว่าคุณจะได้รับมากกว่าหนึ่งพารามิเตอร์ดังนั้น Java วางไว้ในอาร์เรย์ ตัวอย่างเช่นในการดึงพารามิเตอร์เดียวคุณต้องตรวจสอบเนื้อหาของอาร์เรย์ก่อน: 'code' boolean flag = (optionalFlag.length <1)? false: optionalFlag [0];
ซัลวาดอร์วาเลนเซีย

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

2
วิธีนี้ใช้ได้ผลถ้าคุณมีพารามิเตอร์ที่เป็นทางเลือกเพียงตัวเดียว แต่คุณจะต้องตรวจสอบความยาวของอาเรย์และอื่น ๆ ดังนั้นจึงไม่ได้สะอาดมาก: | ถ้าคุณต้องการพารามิเตอร์ทางเลือก "one parameter" คุณก็สามารถประกาศได้สองวิธี (หนึ่ง overloaded doSomething() { doSomething(true); }ไม่มีอาร์เรย์ที่จะจัดการกับไม่มีความกำกวม
rogerdpack

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

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

99

คุณสามารถใช้สิ่งนี้:

public void addError(String path, String key, Object... params) { 
}

paramsตัวแปรเป็นตัวเลือก มันจะถือว่าเป็นอาร์เรย์ที่ไม่มีค่าของวัตถุ

แปลกฉันไม่พบอะไรเกี่ยวกับเรื่องนี้ในเอกสาร แต่มันใช้งานได้!

นี่คือ "ใหม่" ใน Java 1.5 ขึ้นไป (ไม่รองรับใน Java 1.4 หรือเก่ากว่า)

ฉันเห็นผู้ใช้ bhoot พูดถึงเรื่องนี้ด้านล่าง


55

น่าเสียดายที่ Java ไม่รองรับพารามิเตอร์เริ่มต้นโดยตรง

อย่างไรก็ตามฉันได้เขียนชุดของคำอธิบายประกอบ JavaBean และหนึ่งในนั้นสนับสนุนพารามิเตอร์เริ่มต้นดังต่อไปนี้:

protected void process(
        Processor processor,
        String item,
        @Default("Processor.Size.LARGE") Size size,
        @Default("red") String color,
        @Default("1") int quantity) {
    processor.process(item, size, color, quantity);
}
public void report(@Default("Hello") String message) {
    System.out.println("Message: " + message);
}

ตัวประมวลผลคำอธิบายประกอบสร้างวิธีโอเวอร์โหลดเพื่อสนับสนุนสิ่งนี้อย่างถูกต้อง

ดูhttp://code.google.com/p/javadude/wiki/Annotations

ตัวอย่างเต็มรูปแบบที่http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample


53

ไม่มีพารามิเตอร์ทางเลือกใน Java สิ่งที่คุณสามารถทำได้คือการบรรทุกเกินพิกัดฟังก์ชั่นแล้วส่งค่าเริ่มต้น

void SomeMethod(int age, String name) {
    //
}

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}

23

มีการกล่าวถึง VarArgs และการโอเวอร์โหลด ตัวเลือกอื่นคือรูปแบบตัวสร้างซึ่งจะมีลักษณะดังนี้:

 MyObject my = new MyObjectBuilder().setParam1(value)
                                 .setParam3(otherValue)
                                 .setParam6(thirdValue)
                                 .build();

แม้ว่ารูปแบบนั้นจะเหมาะสมที่สุดเมื่อคุณต้องการพารามิเตอร์ทางเลือกในตัวสร้าง


ฉันต้องการเพิ่มการพึ่งพาพารามิเตอร์สำหรับพูดว่า that.let ฉันต้องการ set param3 เฉพาะถ้าฉันตั้ง param1 สำหรับเช่น ฉันต้องการตั้งค่าข้อความความคืบหน้าเฉพาะเมื่อฉันตั้งความคืบหน้าให้มองเห็นได้ isProgressVisible (). setProgressMessage ("กำลังโหลด") ฉันจะประสบความสำเร็จได้อย่างไร
Harshal Bhatt

14

ใน JDK> 1.5 คุณสามารถใช้มันได้

public class NewClass1 {

    public static void main(String[] args) {

        try {
            someMethod(18); // Age : 18
            someMethod(18, "John Doe"); // Age & Name : 18 & John Doe
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void someMethod(int age, String... names) {

        if (names.length > 0) {
            if (names[0] != null) {
                System.out.println("Age & Name : " + age + " & " + names[0]);
            }
        } else {
            System.out.println("Age : " + age);
        }
    }
}

7

คุณสามารถทำสิ่งต่าง ๆ โดยใช้วิธีการมากไปเช่นนี้

 public void load(String name){ }

 public void load(String name,int age){}

นอกจากนี้คุณสามารถใช้คำอธิบายประกอบ @Nullable

public void load(@Nullable String name,int age){}

เพียงแค่ส่งค่า null เป็นพารามิเตอร์แรก

หากคุณผ่านตัวแปรประเภทเดียวกันคุณสามารถใช้สิ่งนี้

public void load(String name...){}

7

เวอร์ชั่นสั้น :

ใช้ สามจุด :

public void foo(Object... x) {
    String first    =  x.length > 0 ? (String)x[0]  : "Hello";
    int duration    =  x.length > 1 ? Integer.parseInt((String) x[1])     : 888;
}   
foo("Hii", ); 
foo("Hii", 146); 

(ขึ้นอยู่กับคำตอบของ @ VitaliiFedorenko)


2

การบรรทุกเกินพิกัดเป็นเรื่องปกติ แต่ถ้ามีตัวแปรจำนวนมากที่ต้องการค่าเริ่มต้นคุณจะพบกับ:

public void methodA(A arg1) {  }
public void methodA( B arg2,) {  }
public void methodA(C arg3) {  }
public void methodA(A arg1, B arg2) {  }
public void methodA(A arg1, C arg3) {  }
public void methodA( B arg2, C arg3) {  }
public void methodA(A arg1, B arg2, C arg3) {  }

ดังนั้นฉันขอแนะนำให้ใช้ Variable Argument ที่จัดทำโดย Java นี่คือลิงค์สำหรับคำอธิบาย


การใช้งาน varargs นั้นถือว่าไม่ดี หากระบบต้องการวิธีดังกล่าวข้างต้นคุณควรคิดถึงการออกแบบใหม่เนื่องจากการออกแบบคลาสดูไม่ดี
Diablo

2
มันยิ่งแย่ไปกว่าการฝึกฝนแก้ตัวให้กับข้อบกพร่องของจาวาและกล่าวหาคนอื่นว่าเป็นการฝึกฝนที่ไม่ดีสำหรับการสังเกตความไม่สะดวกเหล่านั้นและการหาแนวทางแก้ไข
BrianO

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

2

คุณสามารถใช้คลาสที่ทำงานคล้ายกับตัวสร้างเพื่อเก็บค่าทางเลือกของคุณเช่นนี้

public class Options {
    private String someString = "default value";
    private int someInt= 0;
    public Options setSomeString(String someString) {
        this.someString = someString;
        return this;
    }
    public Options setSomeInt(int someInt) {
        this.someInt = someInt;
        return this;
    }
}

public static void foo(Consumer<Options> consumer) {
    Options options = new Options();
    consumer.accept(options);
    System.out.println("someString = " + options.someString + ", someInt = " + options.someInt);
}

ใช้เหมือน

foo(o -> o.setSomeString("something").setSomeInt(5));

ผลผลิตคือ

someString = something, someInt = 5

หากต้องการข้ามค่าที่ไม่จำเป็นทั้งหมดที่คุณต้องเรียกว่าเป็นแบบที่foo(o -> {});คุณชอบหรือคุณต้องการคุณสามารถสร้างfoo()วิธีที่สองที่ไม่ใช้พารามิเตอร์ทางเลือก

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


1

Java ตอนนี้สนับสนุนตัวเลือกใน 1.8 ฉันติดอยู่กับการเขียนโปรแกรมบน Android ดังนั้นฉันใช้ nulls จนกว่าฉันจะ refactor รหัสเพื่อใช้ประเภทตัวเลือก

Object canBeNull() {
    if (blah) {
        return new Object();
    } else {
        return null;
    }
}

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}

ได้โปรดยกตัวอย่าง
sepehr

1

อาร์กิวเมนต์เริ่มต้นไม่สามารถใช้ใน Java เราใช้ C #, C ++ และ Python ได้ที่ไหน

ใน Java เราต้องใช้ 2 เมธอด (ฟังก์ชั่น) แทนหนึ่งด้วยพารามิเตอร์เริ่มต้น

ตัวอย่าง:

Stash(int size); 

Stash(int size, int initQuantity);

http://parvindersingh.webs.com/apps/forums/topics/show/8856498-java-how-to-set-default-parameters-values-like-c-


28
C # รุ่นที่ใหม่กว่าอนุญาตพารามิเตอร์เริ่มต้น
Sogger

2
java มีตัวเลือก "... " สำหรับพารามิเตอร์ใด ๆ ด้วย
Cacho Santa

0

นี่เป็นคำถามเก่า ๆ ที่อาจจะเกิดขึ้นก่อนที่จะมีการแนะนำตัวเลือกประเภทจริง แต่วันนี้คุณสามารถพิจารณาบางสิ่ง: - ใช้วิธีการมากไป - ใช้ตัวเลือกชนิดที่มีข้อดีของการหลีกเลี่ยงการผ่าน NULLs จาก lib บุคคลที่สามเช่น Guava ของ Google การใช้ทางเลือกเป็นพารามิเตอร์ / อาร์กิวเมนต์สามารถพิจารณาได้ว่าเป็นการใช้งานเกินวัตถุประสงค์ตามวัตถุประสงค์หลักคือใช้เป็นเวลาส่งคืน

Ref: https://itcodehub.blogspot.com/2019/06/using-optional-type-in-java.html


0

เราสามารถสร้างพารามิเตอร์ทางเลือกโดยวิธีการบรรทุกเกินพิกัดหรือใช้ DataType ...

| * | วิธีการบรรทุกเกินพิกัด:

RetDataType NameFnc(int NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(String NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(int NamePsgVar1, String NamePsgVar2)
{
    // |* Code Todo *|
    return RetVar;
}

วิธีที่ง่ายที่สุดคือ

| * | DataType ... สามารถเป็นพารามิเตอร์เสริม

RetDataType NameFnc(int NamePsgVar, String... stringOpnPsgVar)
{
    if(stringOpnPsgVar.length == 0)  stringOpnPsgVar = DefaultValue; 

    // |* Code Todo *|
    return RetVar;
}


8
RetDtaTyp... อย่างจริงจัง? เป็นRetDataTypeเรื่องยากที่จะพิมพ์ออกมา?
Alexander - Reinstate Monica

คุณสามารถเพิ่มคำอธิบายเพิ่มเติมลงในรหัสนั้นได้หรือไม่ ตัวแปรแปลก ๆ เหล่านี้ทำอะไร?
Nico Haase

เปลี่ยนชื่อตัวแปรเพื่อให้เข้าใจง่าย ...
Sujay UN

0

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

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}

0

หากเป็นจุดสิ้นสุดของ API วิธีที่สวยงามคือการใช้คำอธิบายประกอบ "Spring":

@GetMapping("/api/foos")
@ResponseBody
public String getFoos(@RequestParam(required = false, defaultValue = "hello") String id) { 
    return innerFunc(id);
}

โปรดสังเกตว่าในกรณีนี้ innerFunc จะต้องการตัวแปรและเนื่องจากไม่ใช่จุดปลาย api จึงไม่สามารถใช้คำอธิบายประกอบ Spring นี้เพื่อให้เป็นตัวเลือก การอ้างอิง: https://www.baeldung.com/spring-request-param

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