Java รองรับค่าพารามิเตอร์เริ่มต้นหรือไม่


1661

ฉันเจอโค้ด Java บางตัวที่มีโครงสร้างต่อไปนี้:

public MyParameterizedFunction(String param1, int param2)
{
    this(param1, param2, false);
}

public MyParameterizedFunction(String param1, int param2, boolean param3)
{
    //use all three parameters here
}

ฉันรู้ว่าใน C ++ ฉันสามารถกำหนดพารามิเตอร์เป็นค่าเริ่มต้น ตัวอย่างเช่น:

void MyParameterizedFunction(String param1, int param2, bool param3=false);

Java รองรับไวยากรณ์ประเภทนี้หรือไม่ มีเหตุผลใดบ้างที่ทำให้ไวยากรณ์สองขั้นตอนนี้เป็นที่ต้องการมากกว่า


85
ไม่ได้รูปแบบของเครื่องมือสร้างสามารถช่วยได้
Dave Jarvis

50
ฉันคิดถึงคุณลักษณะนี้จริงๆ มันช่วยได้มากเมื่อแก้ไขโค้ดที่มีอยู่เพื่อใช้พารามิเตอร์พิเศษกับฟังก์ชันหรือตัวสร้าง
Jatin

4
@Jatin เมื่อทำการเปลี่ยนโครงสร้าง Eclipse "Change method signature" คุณสามารถเพิ่มพารามิเตอร์และระบุค่าเริ่มต้นที่ผู้เรียกใช้ปัจจุบันจะใช้
Erwin Bolwidt

2
@ErwinBolwidt ขอบคุณ ฉันใช้ Android Studio และยังมีตัวเลือกในการเปลี่ยนวิธีการใหม่และให้ค่าเริ่มต้น มีประโยชน์มากทีเดียว
Jatin

3
@temporary_user_name public MyParameterizedFunction(String param1, int param2)เป็นตัวสร้างไม่ใช่วิธีการประกาศ
Mario Ishac

คำตอบ:


955

ไม่โครงสร้างที่คุณพบคือวิธีที่ Java จัดการกับมัน (กล่าวคือมีการโหลดมากเกินไปแทนที่จะเป็นพารามิเตอร์เริ่มต้น)

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



135
@JarrodRoberson: newวิธีการโรงงานคงไม่มีอันตรายมากกว่า พวกเขาจะใช้ตลอดเวลาในรหัสใหม่ ผู้สร้างสำหรับวัตถุที่มีค่าอย่างง่ายมักจะเป็นผลมาจากการใช้วิศวกรรมมากเกินไป
Lii

12
@JarrodRoberson: วิธีที่น่าสนใจในการบังคับให้ใช้งานอย่างถูกต้องผ่านทางคอมไพเลอร์ขอบคุณสำหรับการแบ่งปัน! คำแนะนำที่เป็นมิตรสำหรับการโพสต์ในอนาคต: 300 บรรทัดของซอร์สโค้ดที่ไม่ใส่โค้ดอาจเป็นเรื่องที่ค่อนข้างย่อยสำหรับคนส่วนใหญ่ (รหัสอ่านยากกว่าการเขียนหลังจากทั้งหมด) ขอบคุณอีกครั้ง!
Christian Aichinger

17
@JarrodRoberson: ดีมองไปข้างหน้ามัน! สิ่งที่ฉันต้องการสื่อสาร: ในฐานะผู้อ่านบล็อกของคุณตัวอย่าง 50 บรรทัดพร้อมคำอธิบายข้อความสั้น ๆ ว่าเกิดอะไรขึ้นจะช่วยฉันได้มากกว่า 300 บรรทัดโดยไม่มีบริบท
Christian Aichinger

8
@ user177800 ไม่เห็นด้วย - วิธีการคงที่ถ้าเขียนเป็นฟังก์ชั่นบริสุทธิ์จะดีอย่างสมบูรณ์ ก็เมื่อรัฐคงฟังก์ชั่นการแปรรูปที่พวกเขากลายเป็นปัญหา ...
ลีวายส์ฟูลเลอร์

641

ไม่มี แต่คุณสามารถใช้รูปแบบการสร้างที่อธิบายไว้ในคำตอบนี้กองมากเกิน

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

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

ซึ่งบางฟิลด์สามารถมีค่าเริ่มต้นหรือเป็นทางเลือกก็ได้


141
ในที่สุดตัวอย่างที่ดีน้อยกว่า 2 หน้าของรูปแบบ Builder
nevvermind

14
ฉันอยากรู้ว่าทำไมเราต้องมีคลาสผู้สร้างเมื่อใช้รูปแบบตัวสร้าง ฉันคิดถึง Student s1 = Student ใหม่ (). ชื่อ ("Spicolo"). อายุ (16) .motto ("Aloha, Mr Hand);
ivanceras

52
@ivanceras: มีความเกี่ยวข้องเมื่อคลาสมีฟิลด์ที่ต้องระบุและคุณไม่ต้องการให้อินสแตนซ์ของคลาสเหล่านั้นอยู่ในสถานะที่ไม่ถูกต้อง ดังนั้นหากคุณเพิ่งพูดไปStudent s1 = new Student().age(16);ก็จะทำให้คุณมีนักเรียนที่ไม่มีชื่อซึ่งอาจไม่ดี ถ้ามันไม่ได้แย่วิธีแก้ปัญหาของคุณก็ใช้ได้
Eli Courtwright

57
@ivanceras: อีกเหตุผลหนึ่งคือคุณอาจต้องการให้คลาสของคุณไม่เปลี่ยนรูปหลังจากการก่อสร้างดังนั้นคุณไม่ต้องการวิธีการที่เปลี่ยนค่าของมัน
จูลส์

3
@ivanceras: ฉันใช้ Builders 3 ประการ - กำจัดอาร์กิวเมนต์หลายตัวและการเริ่มต้นที่คล่องแคล่วไม่เปลี่ยนแปลงและที่สำคัญที่สุดฉันรู้สึกถึงการตรวจสอบความถูกต้องของวัตถุโดเมนในวิธีการ build () วิธีการสร้างอินสแตนซ์วัตถุหากไม่ถูกต้อง วิธีการโรงงานเช่นในกรณีข้างต้น buildFreshman (), buildSenior () ฯลฯ
Abhijeet Kushe

485

มีหลายวิธีในการจำลองพารามิเตอร์เริ่มต้นใน 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

    ก) พารามิเตอร์ที่ไม่จำเป็นทั้งหมดอยู่ในประเภทเดียวกัน:

    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

  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 = (String)parameters.get("a");
        } else if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 
    

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


1
คำอธิบายที่ดี ฉันไม่เคยเห็นค่าตอบแทนที่ใช้แบบนี้ 5) สิ่งที่ควรreturn thisทำ นอกจากนี้ไม่FooBuilder().setA("a").build();ตั้งแต่ (ตามคำนิยาม) คอนสตรัคเตอร์ถูกเรียกก่อนและFooBuilder()คืนค่านี่หมายความ.setA("a"):ว่าไม่ได้รับโอกาสที่จะถูกเรียกใช่หรือไม่
Celeritas

3
@Celeritas return thisส่งคืนวัตถุเดียวกันกับที่เรียกว่าเมธอด (ในตัวอย่างFooBuilder) สิ่งนี้ยอมให้มีการโยงเมธอดในคำสั่งเดียวที่ทำหน้าที่บนออบเจ็กต์เดียวกัน: new FooBuilder().setA(..).setB(..).setC(..)ฯลฯ ซึ่งต่างกับการเรียกแต่ละเมธอดในคำสั่งแยกต่างหาก
ADTC

2
@Celeritas new FooBuilder()ส่งคืนFooBuilderวัตถุซึ่งเป็นsetAวิธีการที่เรียกว่า ในฐานะที่setBเป็นไม่ได้เรียกว่าthis.bรักษาค่าเริ่มต้น ในที่สุดbuildวิธีการที่เรียกว่าบนFooBuilderวัตถุนี้ buildวิธีการสร้างและผลตอบแทนของวัตถุที่มีการตั้งค่าให้กับตัวแปรFoo Foo fooขอให้สังเกตว่าFooBuilderวัตถุไม่ได้เก็บไว้ในตัวแปรใด ๆ
ADTC

หมายเหตุยังสามารถใช้เพื่อสร้างพารามิเตอร์เริ่มต้นและมีประโยชน์มากที่สุดเมื่อจำเป็นสำหรับคอลเลกชัน polymorphic docs.oracle.com/javase/tutorial/java/annotations/declaring.html
Martin Spamer

1
มากกว่า 900 upvotes กับคำตอบเดียวกันในสองคำถาม ฉันประทับใจ: stackoverflow.com/questions/965690/java-optional-parameters/…
AdamMc331

256

น่าเศร้าที่ไม่มี


34
มันช่างเศร้าเหลือเกิน การทำเช่นนั้นจะแนะนำลายเซ็นของฟังก์ชันที่อาจคลุมเครือ
Trey

69
@Trey: ภาษาที่มีพารามิเตอร์เริ่มต้นมักจะมีฟังก์ชั่นการทำงานมากเกินไปเนื่องจากมันน่าสนใจน้อยกว่า ดังนั้นไม่มีความกำกวม นอกจากนี้ Scala ยังเพิ่มฟีเจอร์ใน 2.8 และแก้ไขปัญหาความกำกวม (เนื่องจากพวกเขายังคงทำงานหนักเกินไปสำหรับเหตุผลด้านความเข้ากันได้)
PhiLho

32
ฉันล้มเหลวในการดูว่าค่าเริ่มต้นของพารามิเตอร์ป้องกันไม่ให้ฟังก์ชันทำงานหนักเกินไป อินสแตนซ์ C # อนุญาตให้แทนที่และยังอนุญาตให้เริ่มต้นเริ่มต้น ดูเหมือนว่าเป็นทางเลือกโดยพลการไม่ใช่ข้อ จำกัด คือเหตุผล
FlavourScape

51
ใช่ให้แลกกับการทำให้คอมไพเลอร์ทำงานพิเศษและทำให้เราเขียนเกิน 100000 เพื่อให้ผู้ใช้ห้องสมุดของเราสะดวก ความคิดที่ดี.

28
@ user562566: เมื่อใดก็ตามที่ฉันทำงานในโครงการ Java ฉันได้รับความประทับใจว่า Java devs ได้รับค่าตอบแทน / วัดด้วยจำนวนบรรทัดที่ผลิตต่อวัน
Mark K Cowan

83

น่าเสียดายใช่

void MyParameterizedFunction(String param1, int param2, bool param3=false) {}

สามารถเขียนใน Java 1.5 เป็น:

void MyParameterizedFunction(String param1, int param2, Boolean... params) {
    assert params.length <= 1;
    bool param3 = params.length > 0 ? params[0].booleanValue() : false;
}

แต่คุณควรขึ้นอยู่กับว่าคุณรู้สึกอย่างไรกับคอมไพเลอร์ที่สร้าง

new Boolean[]{}

สำหรับการโทรแต่ละครั้ง

สำหรับพารามิเตอร์ที่สามารถตั้งค่าได้หลายตัว:

void MyParameterizedFunction(String param1, int param2, bool param3=false, int param4=42) {}

สามารถเขียนใน Java 1.5 เป็น:

void MyParameterizedFunction(String param1, int param2, Object... p) {
    int l = p.length;
    assert l <= 2;
    assert l < 1 || Boolean.class.isInstance(p[0]);
    assert l < 2 || Integer.class.isInstance(p[1]);
    bool param3 = l > 0 && p[0] != null ? ((Boolean)p[0]).booleanValue() : false;
    int param4 = l > 1 && p[1] != null ? ((Integer)p[1]).intValue() : 42;
}

สิ่งนี้ตรงกับไวยากรณ์ C ++ ซึ่งอนุญาตเฉพาะพารามิเตอร์ที่ผิดนัดในตอนท้ายของรายการพารามิเตอร์

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


14
ฉลาด แต่สามารถใช้ varargs (... ) สำหรับพารามิเตอร์สุดท้ายซึ่ง จำกัด ได้มากกว่าภาษาที่สนับสนุนพารามิเตอร์เริ่มต้นให้คุณ
CurtainDog

6
มันฉลาด แต่ค่อนข้างยุ่งเมื่อเทียบกับรุ่น C ++
บางคนที่ไหนสักแห่ง

5
Java ต้องการพารามิเตอร์ที่เป็นค่าเริ่มต้นเป็นตัวเลือกเนื่องจาก C # และอื่น ๆ อนุญาตให้ ... ไวยากรณ์ชัดเจนและฉันคิดว่าพวกเขาสามารถใช้งานได้อย่างเป็นธรรมเพียงแค่รวบรวมชุดค่าผสมที่เป็นไปได้ทั้งหมด ... ฉันไม่สามารถจินตนาการได้ว่าทำไมพวกเขาจึงไม่เพิ่มภาษา ๆ !
jwl

10
หนึ่งไม่ควรใช้assertในการผลิตรหัส โยนข้อยกเว้น
Michael Dorst

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

38

ไม่ แต่คุณสามารถลอกเลียนแบบได้ง่ายมาก อะไรใน C ++ คือ:

public: void myFunction(int a, int b=5, string c="test") { ... }

ใน Java มันจะเป็นฟังก์ชั่นโอเวอร์โหลด:

public void myFunction(int a, int b, string c) { ... }

public void myFunction(int a, int b) {
    myFunction(a, b, "test");
}

public void myFunction(int a) {
    myFunction(a, 5);
}

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


8
แนวคิดหลักของสัญกรณ์ค่าเริ่มต้นของ C # คือการหลีกเลี่ยงการเข้ารหัสรหัสสำเร็จรูปและมีเพียงคอนสตรัคเตอร์เดียวแทนที่จะเป็นหลาย ๆ
Kolya Ivankov

1
@KolyaIvankov ฉันไม่รู้จัก C # แต่ฉันรู้ C ++ ซึ่งการให้เหตุผลเหมือนกัน ฉันไม่รู้ว่าอะไรจะดีไปกว่า แต่ฉันคิดว่าจริงๆแล้วรหัสต้นแบบเดียวกันนี้ถูกสร้างขึ้นโดยคอมไพเลอร์ในกรณีของ C ++ / C # และมันจะเข้าสู่ไบนารีสุดท้าย
peterh - Reinstate Monica

5
ภาษาการเขียนโปรแกรมทุกภาษา (โดยเฉพาะ) เป็นวิธีหนึ่งในการหลีกเลี่ยงแผ่นสำเร็จรูป Assembler ฉันผิดหรือเปล่า? คำถามก็คือว่ามันจะให้ฟังก์ชั่นที่มีประโยชน์หรือไม่
Kolya Ivankov

2
ประโยคแรกเป็นคำถามเชิงโวหาร คำว่า "คำถาม" ในประโยคที่สองไม่มีส่วนเกี่ยวข้องกับคำถามเชิงโวหารในตอนแรก
Kolya Ivankov

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

24

คุณสามารถทำได้ใน Scala ซึ่งทำงานบน JVM และเข้ากันได้กับโปรแกรม Java http://www.scala-lang.org/

กล่าวคือ

class Foo(var prime: Boolean = false, val rib: String)  {}

58
นำภาษาใหม่มาใช้เพื่อรับคุณสมบัติที่ไม่เหมือนกันใช่ไหม
om-nom-nom

8
@ om-nom-nom: Java ไม่ควรมีอยู่จริง การบอกว่าคุณสมบัติที่ไม่ได้ใช้นั้นเทียบเท่ากับไม่มีใครต้องการมันบอกว่า Java ไม่เป็นที่นิยมก่อนที่มันจะถูกคิดค้นหมายความว่า Gosling ไม่ควรเริ่มต้นการออกแบบ
Val

28
@Val เพียงแค่บอกว่านี้เป็นเหมือนการยิงนกด้วยปืนใหญ่
OM-Nom-Nom

8
ที่ไม่เกี่ยวข้องกับคำถามของ OP
destan

ทำงานใน Kotlin ด้วย และ Groovy และ C # และจาวาสคริปต์ และเกือบทุกภาษาอื่น ๆ ที่ทำขึ้นสำหรับคนจริงและปัญหา
spyro

17

ไม่แต่วิธีที่ง่ายที่สุดในการใช้งานคือ:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    param3 = param3 == null ? false : param3;
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

หรือแทนผู้ประกอบการที่ประกอบไปด้วยคุณสามารถใช้if:

public myParameterizedFunction(String param1, int param2, Boolean param3) {

    if (param3 == null) {
        param3 = false;
    }
}

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

2
ใช่วิธีนี้ดูเหมือนจะดีที่สุดจากทางเลือกอื่น ๆ ยังคงเป็นการดีสำหรับ Java ที่จะใช้ค่าเริ่มต้น Kotlin แสดงให้เห็นว่าสิ่งนี้สามารถทำได้ดังนั้นฉันไม่แน่ใจว่าทำไมออราเคิลไม่เข้าสู่ยุคสมัยใหม่ : D
shevy

13

ฉันอาจระบุชัดเจนที่นี่ แต่ทำไมไม่เพียงแค่ใช้พารามิเตอร์ "เริ่มต้น" ด้วยตัวคุณเอง?

public class Foo() {
        public void func(String s){
                func(s, true);
        }
        public void func(String s, boolean b){
                //your code here
        }
}

สำหรับค่าเริ่มต้นคุณจะใช้อย่างใดอย่างหนึ่ง

func("my string");

และหากคุณไม่ต้องการใช้ค่าเริ่มต้นคุณก็จะใช้

func("my string", false);

11
ผู้โพสต์ถามว่าสามารถหลีกเลี่ยงรูปแบบนี้ (ค่อนข้างน่าเกลียด) ... ;-) ในภาษาที่ทันสมัยกว่า (เช่น c #, Scala) คุณไม่จำเป็นต้องมีการโอเวอร์โหลดพิเศษนี้ซึ่งสร้างรหัสบรรทัดเพิ่มเติมเท่านั้น จนถึงบางจุดคุณสามารถใช้ varargs ในระหว่างนี้ (static int max (array ... int)) {}) แต่มันก็เป็นวิธีการแก้ปัญหาที่น่าเกลียดมากเท่านั้น
Offler

2
การบรรทุกเกินพิกัดไม่น่าเกลียดและมีประโยชน์มากมายเช่นการเรียกใช้เมธอดที่แตกต่างกันซึ่งมีลายเซ็นที่แตกต่างกันสามารถทำงานได้หลากหลาย //This is better public class Foo() { /* This does something */ public void func(String s){ //do something } /* This does something else with b */ public void func(String s, boolean b){ // b was passed } } //Than this public class Foo() { /* This does something unless b = value, then it does something else */ public void func(String s, boolean b = value){ If (b){ // Do Something } else{ // Do something else } } }
Antony Booth

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

พารามิเตอร์เริ่มต้น @Offler ไม่เกี่ยวข้องกับ "ภาษาที่ทันสมัย" ฉันใช้มันใน Delphi เมื่อ 20 ปีก่อนและพวกมันอาจมีอยู่แล้วใน Turbo Pascal
เหลือเชื่อ

ฉันเห็นด้วยกับ Offler และไม่เห็นด้วยกับ Antony Booth ฉันพบว่าไม่เพียง แต่น่าเกลียด แต่ยังไร้ประสิทธิภาพพอสมควร ภาษาเช่น ruby ​​หรือ python ทำให้ใช้งานพารามิเตอร์เริ่มต้นได้ง่าย ฉันเดาว่า Java ต้องการให้คุณทำอะไรเพื่อหาวิธีแก้ปัญหา (และใช้) การตรวจสอบอย่างชัดเจนเมื่อเทียบกับ null ดูเหมือนจะเป็นทางเลือกที่น่าเกลียดน้อยที่สุดเนื่องจากฉันอาจเรียกมันว่าจาก commandline ด้วย (แค่ไม่ได้ให้อะไรเลยและจัดการกับตัวแปรที่มีสติ); ดูเหมือนว่าวิธีการโอเวอร์โหลดของโอเปอเรเตอร์ ... ละเอียดมาก (เช่นอย่างน้อย 3 บรรทัดเมื่อเทียบกับ null-check และอีกบรรทัดถ้าโค้ดมีความซับซ้อนมากขึ้น)
shevy

8

ดังที่ Scala ถูกกล่าวถึงKotlinก็มีมูลค่าการกล่าวขวัญเช่นกัน ในพารามิเตอร์ฟังก์ชัน Kotlin สามารถมีค่าเริ่มต้นได้เช่นกันและสามารถอ้างอิงพารามิเตอร์อื่น ๆ ได้:

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
    ...
}

เช่น Scala Kotlin ทำงานบน JVM และสามารถรวมเข้ากับโครงการ Java ที่มีอยู่ได้อย่างง่ายดาย


6

เลขที่

คุณสามารถบรรลุพฤติกรรมเดียวกันโดยส่งผ่าน Object ที่มีค่าเริ่มต้นอัจฉริยะ แต่มันก็ขึ้นอยู่กับสถานการณ์ของคุณ


5

ไม่โดยทั่วไป Java นั้นมีน้ำตาลประโยคไม่มากนักเนื่องจากพวกเขาพยายามทำภาษาง่าย ๆ


26
ไม่มาก ความจริงที่ขมขื่นก็คือทีมอยู่ในช่วงเวลาที่ จำกัด และไม่มีเวลาทำน้ำตาล เหตุใดจึงมีคำอื่นconstและgotoสงวนไว้ซึ่งไม่มีการใช้งาน - โดยเฉพาะอย่างยิ่งconstเป็นสิ่งที่ฉันพลาดอย่างขมขื่น - finalไม่มีอะไรทดแทนและพวกเขารู้ - และถ้าคุณทำสติตัดสินใจที่จะไม่ดำเนินการgotoที่คุณจะไม่จำเป็นต้องขอสงวนคำหลัก - และต่อมาในทีม Java โกงโดยการทำป้ายชื่อตามbreakและมีประสิทธิภาพเป็นปาสกาลcontinue goto
Martin

"Simple, Object-Oriented และ Familiar" เป็นเป้าหมายการออกแบบแน่นอน - ดูoracle.com/technetwork/java/intro-141325.html
mikera

1
tomjen พูดว่า: "ไม่. โดยทั่วไป Java นั้นมีน้ำตาลประโยคไม่มากนักเนื่องจากพวกเขาพยายามทำภาษาง่าย ๆ " ดังนั้นคุณกำลังบอกว่าการลบฟีเจอร์ที่ไม่จำเป็นออกจาก C ++ ทำให้ Java เป็นภาษาง่าย ๆ แล้วบอกฉันหน่อยว่าทำไม Java ถึงมีวิธีการแปรผัน? ทำไมมันถึงมีค่า varargs ไม่จำเป็นถ้าคุณสามารถใช้อาร์เรย์ของวัตถุแทนได้ไหม? ดังนั้นสามารถลบ varargs ออกจากภาษาได้เนื่องจากไม่จำเป็น สิ่งนี้จะทำให้ Java ง่ายกว่าที่เป็นอยู่ในตอนนี้ ฉันถูกไหม? การบรรทุกเกินพิกัดยังสามารถลบออกได้เนื่องจากคุณมีชื่อไม่สิ้นสุดสำหรับแต่ละวิธี

1
และนั่นคือเหตุผลที่เราได้รับฤดูใบไม้ผลิและสิ่งที่ใช้ภาษาที่เรียบง่ายและเปิดโครงการใด Java จริงใน clusterfuck ของไวยากรณ์และสำเร็จรูป :-)
matanster

การบังคับให้ devs ใช้โค้ดสำเร็จรูปและวิธีแก้ปัญหาที่ซับซ้อนไม่เข้าใจคำว่า "ง่าย" myFunction (a, b = false, c = 3) นั่นคือสิ่งที่ฉันเรียกว่าง่าย
spyro

5

แทนที่จะใช้:

void parameterizedMethod(String param1, int param2) {
    this(param1, param2, false);
}

void parameterizedMethod(String param1, int param2, boolean param3) {
    //use all three parameters here
}

คุณสามารถใช้ประโยชน์จากฟังก์ชั่นเสริมของ java ได้โดยใช้วิธีการเดียว:

void parameterizedMethod(String param1, int param2, @Nullable Boolean param3) {
    param3 = Optional.ofNullable(param3).orElse(false);
    //use all three parameters here
}

ความแตกต่างที่สำคัญคือคุณต้องใช้คลาส wrapper แทนประเภท Java ดั้งเดิมเพื่อให้nullอินพุต Booleanแทนboolean, Integerแทนintและอื่น ๆ


4

ไม่รองรับ แต่มีหลายตัวเลือกเช่นการใช้รูปแบบวัตถุพารามิเตอร์ด้วยไวยากรณ์น้ำตาล:

public class Foo() {
    private static class ParameterObject {
        int param1 = 1;
        String param2 = "";
    }

    public static void main(String[] args) {
        new Foo().myMethod(new ParameterObject() {{ param1 = 10; param2 = "bar";}});
    }

    private void myMethod(ParameterObject po) {
    }
}

ในตัวอย่างนี้เราสร้างParameterObjectด้วยค่าเริ่มต้นและแทนที่พวกเขาในส่วนเริ่มต้นอินสแตนซ์คลาส{ param1 = 10; param2 = "bar";}



3

คุณอาจใช้เครื่องมือสร้างวิธีการเรียกใช้จาวาเพื่อสร้างตัวสร้างโดยอัตโนมัติด้วยค่าเริ่มต้น

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

@GenerateMethodInvocationBuilder
public class CarService {
 public CarService() {
 }

 public String getCarsByFilter(//
   @Default("Color.BLUE") Color color, //
   @Default("new ProductionYear(2001)") ProductionYear productionYear,//
   @Default("Tomas") String owner//
 ) {
  return "Filtering... " + color + productionYear + owner;
 }
}

จากนั้นคุณสามารถเรียกใช้เมธอด

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .invoke(instance);

หรือตั้งค่าเริ่มต้นใด ๆ เป็นอย่างอื่น

CarService instance = new CarService();
String carsByFilter = CarServiceGetCarsByFilterBuilder.getCarsByFilter()//
  .withColor(Color.YELLOW)//
  .invoke(instance);

2

วิธีการที่คล้ายกันกับhttps://stackoverflow.com/a/13864910/2323964ที่ใช้งานได้ใน Java 8 คือการใช้อินเทอร์เฟซที่มี getters เริ่มต้น นี่จะเป็นช่องว่างที่ละเอียดมากขึ้น แต่เป็นการเยาะเย้ยและเป็นเรื่องที่ดีมากเมื่อคุณมีอินสแตนซ์จำนวนมากที่คุณต้องการดึงความสนใจไปที่พารามิเตอร์

public class Foo() {
    public interface Parameters {
        String getRequired();
        default int getOptionalInt(){ return 23; }
        default String getOptionalString(){ return "Skidoo"; }
    }

    public Foo(Parameters parameters){
        //...
    }

    public static void baz() {
        final Foo foo = new Foo(new Person() {
            @Override public String getRequired(){ return "blahblahblah"; }
            @Override public int getOptionalInt(){ return 43; }
        });
    }
}

2

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

int foo(int a) {
    // do something with a
    return a;
}

int foo() {
    return foo(0); // here, 0 is a default value for a
}

1

นี่คือวิธีที่ฉันทำ ... มันไม่สะดวกบางทีอาจจะมี 'อาร์กิวเมนต์ที่ไม่จำเป็น' เทียบกับพารามิเตอร์ที่คุณกำหนด แต่มันทำให้งานเสร็จ:

public void postUserMessage(String s,boolean wipeClean)
{
    if(wipeClean)
    {
        userInformation.setText(s + "\n");
    }
    else
    {
        postUserMessage(s);
    }
}

public void postUserMessage(String s)
{
    userInformation.appendText(s + "\n");
}

แจ้งให้ทราบล่วงหน้าฉันสามารถเรียกชื่อวิธีการเดียวกันกับทั้งสตริงหรือฉันสามารถเรียกมันด้วยสตริงและค่าบูลีน ในกรณีนี้การตั้งค่า cleanClean เป็น true จะแทนที่ข้อความทั้งหมดใน TextArea ของฉันด้วยสตริงที่จัดเตรียมไว้ การตั้งค่า wipeClean เป็น false หรือปล่อยให้ทั้งหมดเข้าด้วยกันเพียงแค่ต่อท้ายข้อความที่ให้ไว้กับ TextArea

แจ้งให้ทราบว่าฉันไม่ได้ทำซ้ำรหัสในสองวิธีฉันเพียงแค่เพิ่มการทำงานของความสามารถในการรีเซ็ต TextArea โดยการสร้างวิธีการใหม่ที่มีชื่อเดียวกันกับบูลีนที่เพิ่ม

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


1

ไม่ แต่เรามีทางเลือกในรูปแบบของฟังก์ชั่นการโหลดมากเกินไป

เรียกว่าเมื่อไม่มีการส่งพารามิเตอร์

void operation(){

int a = 0;
int b = 0;

} 

เรียกว่าเมื่อผ่านพารามิเตอร์ "a"

void operation(int a){

int b = 0;
//code

} 

เรียกว่าเมื่อพารามิเตอร์ b ผ่าน

void operation(int a , int b){
//code
} 

1

มีปัญหาครึ่งโหลหรือดีกว่าเช่นนี้ในที่สุดคุณมาถึงรูปแบบสแตติกจากโรงงาน ... ดู crypto API สำหรับสิ่งนั้น เรียงลำดับยากที่จะอธิบาย แต่คิดอย่างนี้: ถ้าคุณมีคอนสตรัคเตอร์ค่าเริ่มต้นหรืออย่างอื่นวิธีเดียวที่จะเผยแพร่สถานะเกินกว่าเครื่องหมายปีกกาแบบโค้งก็คือให้บูลีน isValid; (พร้อมกับ null เป็นค่าเริ่มต้นที่ v คอนสตรัคเตอร์ที่ล้มเหลว) หรือโยนข้อยกเว้นที่ไม่เคยให้ข้อมูลเมื่อนำกลับจากผู้ใช้ฟิลด์

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

ฉันคิดว่าสิ่งที่เราต้องทำที่นี่คือปัญหาของค่า null เป็นค่าเริ่มต้น vis-a-vis บางสิ่งบางอย่าง String one = new String (""); เป็นตัวแปรสมาชิกจากนั้นทำการตรวจสอบโมฆะก่อนที่จะกำหนดสตริงที่ส่งผ่านไปยังตัวสร้าง

น่าทึ่งมากเกี่ยวกับปริมาณของวิทยาการคอมพิวเตอร์ทางสตราโตสเฟียร์ที่มีอยู่ในจาวา

C ++ และอื่น ๆ มี libs ของผู้ขายใช่ Java สามารถวิ่งเร็วกว่าบนเซิร์ฟเวอร์ขนาดใหญ่เนื่องจากเป็นกล่องเครื่องมือขนาดใหญ่ ศึกษาบล็อก initializer แบบคงที่อยู่กับเรา



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