Câu hỏi Java có hỗ trợ các giá trị tham số mặc định không?


Tôi đã gặp một số mã Java có cấu trúc sau:

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

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

Tôi biết rằng trong C + + tôi có thể gán một tham số một giá trị mặc định. Ví dụ:

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

Java có hỗ trợ loại cú pháp này không? Có bất kỳ lý do nào tại sao cú pháp hai bước này là thích hợp hơn không?


1313
2018-06-15 18:04


gốc


Không. Tuy nhiên, mẫu Builder có thể giúp bạn. - Dave Jarvis
Tôi thực sự nhớ tính năng này. Nó giúp ích rất nhiều khi sửa đổi mã hiện có để lấy tham số bổ sung cho hàm hoặc hàm tạo - Jatin
@Jatin Với việc tái cấu trúc "Thay đổi chữ ký phương thức" của Eclipse, bạn có thể thêm một tham số và cung cấp một giá trị mặc định mà người dùng hiện có sẽ sử dụng. - Erwin Bolwidt
@ErwinBolwidt Cảm ơn. Tôi đang sử dụng Android Studio và nó cũng có tùy chọn refactoring phương pháp và cung cấp các giá trị mặc định. Khá hữu dụng. - Jatin


Các câu trả lời:


Không, cấu trúc bạn tìm thấy là cách Java xử lý nó (có nghĩa là, với quá tải thay vì các tham số mặc định).

Đối với các nhà thầu, Xem Java hiệu quả: Hướng dẫn ngôn ngữ lập trình Mẹo 1 mục (Xem xét các phương thức nhà máy tĩnh thay vì các nhà xây dựng) nếu quá tải đang trở nên phức tạp. Đối với các phương pháp khác, đổi tên một số trường hợp hoặc sử dụng một đối tượng tham số có thể giúp đỡ. Đây là khi bạn có đủ phức tạp mà phân biệt là khó khăn. Trường hợp xác định là nơi bạn phải phân biệt bằng thứ tự các tham số, không chỉ số và loại.


758
2018-06-15 18:14



đây là vào năm 2009, static bất cứ điều gì là khá nhiều được coi là có hại trong năm 2015. Nhập thông thạo an toàn Builder các trường hợp thực thi các hợp đồng xây dựng hoàn chỉnh và hợp lệ là một giải pháp tốt hơn nhiều ngay bây giờ. - feeling unwelcome
@JarrodRoberson: Phương pháp nhà máy tĩnh không có hại nhiều hơn new. họ đang đã sử dụng tất cả thời gian trong mã mới. Các nhà xây dựng cho các đối tượng giá trị đơn giản thường là kết quả của kỹ thuật quá mức. - Lii
@JarrodRoberson: Cách thú vị để buộc sử dụng đúng thông qua trình biên dịch, cảm ơn bạn đã chia sẻ! Đề xuất thân thiện cho các bài viết trong tương lai: 300 dòng mã nguồn không được chú ý có lẽ là một chút tiêu hóa cho hầu hết mọi người (mã khó đọc hơn viết). Cảm ơn một lần nữa! - Christian Aichinger
@ChristianAichinger - Tôi có một kỹ thuật tiên tiến thậm chí nghiêm ngặt hơn tôi đang sử dụng bây giờ kết hợp các giao diện chung với các lớp trừu tượng với các nhà xây dựng tư nhân mà tôi đang viết lên như một bài đăng trên blog. Liên kết blog của tôi nằm trong tiểu sử của tôi. như một sang một bên, tự tài liệu mã không cần ý kiến ​​:) - feeling unwelcome
@ JarrodRoberson: Nice, mong chờ nó! Những gì tôi muốn giao tiếp: với tư cách là người đọc blog của bạn, ví dụ 50 dòng có mô tả ngắn gọn về nội dung đang diễn ra sẽ giúp tôi hơn 300 dòng không có ngữ cảnh. - Christian Aichinger


Không, nhưng bạn có thể sử dụng Mẫu Builder, như được mô tả trong câu trả lời Stack Overflow này.

Như được mô tả trong câu trả lời được liên kết, Mẫu Builder cho phép bạn viết mã như

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

trong đó một số trường có thể có giá trị mặc định hoặc có thể là tùy chọn.


544
2018-06-15 19:20



Cuối cùng, ví dụ nhỏ hơn 2 trang nhỏ của mẫu Builder. - nevvermind
Im tò mò, mặc dù tại sao chúng ta cần một lớp xây dựng khi sử dụng các mô hình xây dựng. Tôi đã nghĩ đến sinh viên s1 = new Student (). Tên ("Spicolo"). Tuổi (16) .motto ("Aloha, Mr Hand); - ivanceras
@ivanceras: Nó có liên quan khi các lớp có các trường bắt buộc và bạn không muốn có thể khởi tạo các lớp đó ở trạng thái không hợp lệ. Vì vậy, nếu bạn vừa nói Student s1 = new Student().age(16); sau đó điều đó sẽ để lại cho bạn một Sinh viên không có tên, điều đó có thể là xấu. Nếu nó không phải là xấu, sau đó giải pháp của bạn là tốt. - Eli Courtwright
@ivanceras: một lý do khác là bạn có thể muốn lớp học của bạn trở nên bất biến sau khi xây dựng, vì vậy bạn sẽ không muốn các phương pháp trong đó thay đổi giá trị của nó. - Jules
@ivanceras: Tôi đã sử dụng Trình xây dựng cho 3 thứ - loại bỏ nhiều đối số và khởi tạo trôi chảy, bất biến và quan trọng nhất là tôi cảm thấy xác thực đối tượng miền trong phương thức build () .Tại sao tạo một đối tượng nếu không hợp lệ.Bạn cũng có thể quá tải tĩnh các phương thức của nhà máy như trong trường hợp trên buildFreshman (), buildSenior () vv - Abhijeet Kushe


Có một số cách để mô phỏng các tham số mặc định trong Java:

  1. Phương pháp quá tải.

    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");
    

    Một trong những hạn chế của phương pháp này là nó không hoạt động nếu bạn có hai tham số tùy chọn cùng loại và bất kỳ tham số nào trong số chúng có thể được bỏ qua.

  2. Varargs.

    a) Tất cả các tham số tùy chọn đều có cùng loại:

    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) Các loại tham số tùy chọn có thể khác nhau:

    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");
    

    Hạn chế chính của phương pháp này là nếu các tham số tùy chọn thuộc các kiểu khác nhau, bạn sẽ mất kiểm tra kiểu tĩnh. Hơn nữa, nếu mỗi tham số có ý nghĩa khác nhau, bạn cần một số cách để phân biệt chúng.

  3. Nulls. Để giải quyết các hạn chế của các phương pháp trước đó, bạn có thể cho phép các giá trị null và sau đó phân tích từng tham số trong một thân phương thức:

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

    Bây giờ tất cả các giá trị đối số phải được cung cấp, nhưng các giá trị mặc định có thể là rỗng.

  4. Lớp tùy chọn. Cách tiếp cận này tương tự như null, nhưng sử dụng Java 8 Lớp tùy chọn cho các tham số có giá trị mặc định:

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

    Tùy chọn làm cho một hợp đồng phương thức rõ ràng cho người gọi, tuy nhiên, người ta có thể thấy chữ ký đó quá dài dòng.

  5. Mô hình trình tạo. Các mô hình xây dựng được sử dụng cho các nhà xây dựng và được thực hiện bằng cách giới thiệu một lớp Builder riêng biệt:

     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. Bản đồ. Khi số lượng tham số quá lớn và đối với hầu hết các giá trị mặc định thường được sử dụng, bạn có thể chuyển đối số phương thức dưới dạng bản đồ của tên / giá trị của chúng:

    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")); 
    

Xin lưu ý rằng bạn có thể kết hợp bất kỳ phương pháp nào trong số các phương pháp này để đạt được kết quả mong muốn.


349
2017-11-01 02:03



Lời giải thích hay. Tôi chưa bao giờ thấy giá trị trả lại được sử dụng như thế này. Đối với 5) những gì làm return this làm gì? Ngoài ra, không FooBuilder().setA("a").build(); vì (theo định nghĩa) hàm tạo được gọi là đầu tiên và FooBuilder() trả về một giá trị, điều này không có nghĩa là .setA("a"): không có cơ hội được gọi? - Celeritas
@Celeritas return this trả về cùng một đối tượng mà phương thức được gọi (trong ví dụ này, FooBuilder). Điều này cho phép chuỗi các phương thức trong một câu lệnh hoạt động trên cùng một đối tượng: new FooBuilder().setA(..).setB(..).setC(..) vv trái ngược với việc gọi từng phương thức trong một câu lệnh riêng biệt. - ADTC
@Celeritas new FooBuilder() trả về một FooBuilder đối tượng mà trên đó setA phương thức được gọi. Như setB không được gọi, this.b giữ lại giá trị mặc định. Cuối cùng build phương pháp được gọi là FooBuilder vật. Các build phương thức tạo và trả về Foo đối tượng được đặt thành biến Foo foo. Lưu ý rằng FooBuilder đối tượng không được lưu trữ trong bất kỳ biến nào. - ADTC
Chú thích cũng có thể được sử dụng để tạo các tham số mặc định và hữu ích nhất khi được yêu cầu cho các bộ sưu tập đa hình. docs.oracle.com/javase/tutorial/java/annotations/declaring.html - Martin Spamer
Hơn 900 upvotes trên cùng một câu trả lời qua hai câu hỏi. Tôi rất ấn tượng: stackoverflow.com/questions/965690/java-optional-parameters/… - AdamMc331


Thật đáng buồn không.


197
2018-06-15 18:05



Có buồn không? Làm như vậy sẽ giới thiệu các chữ ký chức năng có thể mơ hồ. - Trey
@ Trey: các ngôn ngữ có thông số mặc định thường xuyên quá tải chức năng mương vì nó sau đó ít hấp dẫn hơn. Vì vậy, không mơ hồ. Bên cạnh đó, Scala đã thêm tính năng này vào phiên bản 2.8 và bằng cách nào đó đã giải quyết được vấn đề mơ hồ (vì chúng giữ nguyên quá tải vì lý do tương thích). - PhiLho
Tôi không thấy cách mặc định tham số ngăn quá tải chức năng. C # ví dụ cho phép ghi đè và cũng cho phép khởi tạo mặc định. Có vẻ như sự lựa chọn tùy ý, không hạn chế là lý do. - FlavorScape
Yeah, cho phép giao dịch làm cho trình biên dịch làm một số công việc phụ và thay vào đó làm cho tất cả chúng ta viết 100000 quá tải để cung cấp cho người dùng thư viện của chúng tôi thuận tiện. Ý tưởng tốt.
@ user562566: Bất cứ khi nào tôi làm việc trên một dự án Java, tôi nhận được ấn tượng rằng các nhà phát triển Java được trả tiền / được đo lường bằng bao nhiêu dòng mã họ sản xuất mỗi ngày - Mark K Cowan


Không may là đúng vậy.

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

có thể được viết bằng Java 1.5 dưới dạng:

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

Nhưng có hay không bạn nên phụ thuộc vào cách bạn cảm nhận về trình biên dịch tạo ra một

new Boolean[]{}

cho mỗi cuộc gọi.

Đối với nhiều thông số mặc định:

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

có thể được viết bằng Java 1.5 dưới dạng:

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;
}

Điều này khớp với cú pháp C ++, chỉ cho phép các tham số mặc định ở cuối danh sách tham số.

Ngoài cú pháp, có một sự khác biệt mà điều này đã chạy kiểm tra kiểu thời gian cho các tham số mặc định được truyền và kiểu C ++ kiểm tra chúng trong khi biên dịch.


76
2018-05-26 21:51



Thông minh, nhưng varargs (...) chỉ có thể được sử dụng cho tham số cuối cùng, đó là hạn chế hơn những gì các ngôn ngữ hỗ trợ các tham số mặc định cung cấp cho bạn. - CurtainDog
đó là thông minh nhưng hơi lộn xộn so với phiên bản C ++ - Someone Somewhere
Java chắc chắn cần các tham số mặc định tùy chọn như C # và những thứ khác cho phép ... cú pháp là hiển nhiên và tôi cho rằng chúng có thể thực hiện điều này khá đơn giản bằng cách chỉ biên dịch tất cả các kết hợp có thể ... Tôi không thể tưởng tượng được tại sao chúng chưa thêm nó vào ngôn ngữ chưa! - jlarson
Người ta không bao giờ nên sử dụng assert trong mã sản xuất. Ném một ngoại lệ. - anthropomorphic
-1 Điều này thực sự không phải là những gì varargs là cho. Đây là một hack. - trong trường hợp này, việc sử dụng quá tải sẽ là cách dễ đọc hơn (điều này thật không may, vì ba ký tự phụ có thể đọc được nhiều hơn 5 dòng nguồn khác ...). - nhưng Java không hỗ trợ các tham số mặc định. - BrainSlugs83


Không, nhưng bạn có thể dễ dàng cạnh tranh với họ. Những gì trong C ++ là:

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

Trong Java, nó sẽ là một hàm bị quá tải:

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);
}

Trước đó đã được đề cập, các tham số mặc định đã gây ra các trường hợp không rõ ràng trong quá tải hàm. Điều đó chỉ đơn giản là không đúng, chúng ta có thể thấy trong trường hợp của C ++: có, có thể nó có thể tạo ra các trường hợp mơ hồ, nhưng những vấn đề này có thể dễ dàng xử lý. Nó đơn giản không được phát triển trong Java, có lẽ bởi vì những người sáng tạo muốn có một ngôn ngữ đơn giản hơn nhiều như C ++ - nếu họ có đúng, là một câu hỏi khác. Nhưng hầu hết chúng ta không nghĩ anh ta sử dụng Java vì tính đơn giản của nó.


35
2017-11-04 09:12



Ý tưởng chính của ký hiệu giá trị mặc định C # là chính xác để tránh mã hóa bản mẫu này và chỉ có một hàm tạo thay vì nhiều. - Kolya Ivankov
@KolyaIvankov Tôi không biết C #, nhưng tôi biết C ++ nơi lý luận là như nhau. Tôi không biết những gì là tốt hơn, nhưng tôi nghĩ, thực sự cùng một mã boilerplate được tạo ra bởi trình biên dịch trong trường hợp của C ++ / C # và nó sẽ đi vào nhị phân cuối cùng. - peterh
Mỗi ngôn ngữ lập trình là (đặc biệt) một phương tiện để tránh một bản mẫu Assembler, tôi có sai không? Câu hỏi đặt ra là liệu nó có mang lại một chức năng hữu ích hay không. - Kolya Ivankov
@KolyaIvankov Tôi không thực sự hiểu câu hỏi của bạn. Nếu bạn có câu hỏi, hãy đặt câu hỏi này như một câu hỏi. - peterh
Câu đầu tiên là một câu hỏi tu từ. Từ "câu hỏi" trong câu thứ hai không liên quan gì đến câu hỏi tu từ trong câu đầu tiên. - Kolya Ivankov


Bạn có thể làm điều này là trong Scala, chạy trên JVM và tương thích với các chương trình Java. http://www.scala-lang.org/

I E.

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

19
2017-07-21 16:26



Mang theo toàn bộ ngôn ngữ mới để có được một tính năng không phổ biến? - om-nom-nom
@ om-nom-nom: Java không bao giờ tồn tại. Nói rằng một tính năng không được sử dụng tương đương mà không ai cần nó nói rằng Java không phổ biến trước khi nó được phát minh có nghĩa là Gosling không nên bắt đầu thiết kế nó. - Val
@Val chỉ nói rằng điều này giống như bắn chim với pháo - om-nom-nom
không liên quan gì đến câu hỏi của OP - destan


Tôi có thể nói rõ ràng ở đây nhưng tại sao không chỉ đơn giản là thực hiện các "mặc định" tham số chính mình?

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

cho mặc định bạn sẽ sử dụng ether

func ("chuỗi của tôi");

và nếu bạn không muốn sử dụng mặc định, bạn sẽ sử dụng

func ("chuỗi của tôi", sai);


11
2018-02-18 13:59



Các poster hỏi nếu điều này (khá xấu xí) mô hình có thể tránh được ... ;-) Trong nhiều ngôn ngữ hiện đại (như c #, Scala) bạn không cần quá tải thêm này mà chỉ tạo ra nhiều dòng mã. Đến một số điểm bạn có thể sử dụng varargs trong khi chờ đợi (static int max (int ... array) {}), nhưng chúng chỉ là một giải pháp rất xấu. - Offler
Quá tải không phải là xấu và có nhiều lợi ích, chẳng hạn như các cuộc gọi phương thức khác nhau với các chữ ký khác nhau có thể thực hiện các chức năng khác nhau. //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
Vâng, nếu ai muốn hành vi khác nhau. Nếu sự khác biệt duy nhất là một sự thay đổi nhỏ trong tính toán, vv, nó chắc chắn là một sự lãng phí nỗ lực để tạo ra nhiều chữ ký. Mặc định có ý nghĩa khi bạn cần chúng ... và thiếu nó không nên được phân loại như là một yêu cầu "vô dụng". - Kapil
Các thông số mặc định @Offler không liên quan gì đến "ngôn ngữ hiện đại". Tôi đã sử dụng chúng ở Delphi cách đây 20 năm và chúng có thể đã tồn tại trong Turbo Pascal. - The incredible Jan


Không. Nói chung, Java không có nhiều cú pháp, vì họ đã cố gắng tạo ra một ngôn ngữ đơn giản.


6
2018-06-15 18:49



Không hẳn. Sự thật cay đắng là nhóm nghiên cứu đã có lịch biểu chặt chẽ và không có thời gian để viết cú pháp. Tại sao khác sẽ const và goto được đặt trước từ khóa mà không thực hiện? - Đặc biệt const là điều tôi nhớ cay đắng - final không thay thế và họ biết điều đó. - Và nếu bạn làm láu lỉnh quyết định không bao giờ triển khai goto bạn sẽ không cần phải đặt trước từ khóa. - Và sau đó trong Nhóm Java bị lừa bằng cách tạo ra Nhãn dựa trên break và continue mạnh mẽ như một Pascal goto. - Martin
"Đơn giản, hướng đối tượng và quen thuộc" thực sự là một mục tiêu thiết kế - xem oracle.com/technetwork/java/intro-141325.html - mikera
tomjen nói: "Không. Nói chung Java không có nhiều cú pháp (bất kỳ) đường nào, vì chúng cố gắng tạo ra một ngôn ngữ đơn giản". Vì vậy, bạn đang nói rằng việc loại bỏ rất nhiều tính năng không cần thiết từ C ++ làm cho một ngôn ngữ Java trở thành một ngôn ngữ đơn giản, sau đó cho tôi biết tại sao Java lại có các phương thức variadic? Tại sao nó có varargs? Nó không phải là cần thiết nếu bạn chỉ có thể sử dụng một mảng các đối tượng thay vào đó, tôi có phải không? Vì vậy, varargs có thể được loại bỏ khỏi ngôn ngữ, bởi vì nó là không cần thiết. Điều này sẽ làm cho Java đơn giản hơn so với những gì nó bây giờ. Tôi có đúng không? Quá tải cũng có thể được loại bỏ, bởi vì bạn có tên vô hạn cho mỗi phương pháp. - Farewell Stack Exchange
@Martin có ở đâu để đọc về câu chuyện đằng sau Java không? Tôi tò mò muốn biết thêm từ bình luận của bạn. - Daniel Soutar