Các tham số tùy chọn Java


813

Làm cách nào để sử dụng các tham số tùy chọn trong Java? Thông số kỹ thuật hỗ trợ các tham số tùy chọn?

Câu trả lời:


517

varargs có thể làm điều đó (theo một cách nào đó). Ngoài ra, tất cả các biến trong khai báo của phương thức phải được cung cấp. Nếu bạn muốn một biến là tùy chọn, bạn có thể nạp chồng phương thức bằng chữ ký không yêu cầu tham số.

private boolean defaultOptionalFlagValue = true;

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

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

Theo tôi, quá tải là một câu trả lời tốt, trong khi varargs là một câu trả lời rất tệ. Vui lòng xóa nhận xét về varargs hoặc giải thích nhược điểm nghiêm trọng gây ra bởi khả năng có nhiều hơn một giá trị trả về.
Andreas Vogl

Thêm các biến toàn cục có thể gây ra một số vấn đề khác.
Helen Cui

1652

Có một số cách để mô phỏng các tham số tùy chọn 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. Biến thể.

    a) Tất cả các tham số tùy chọn là 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 loại 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 cách tiếp cận 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 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à null.

  4. Lớp học tùy chọn. Cách tiếp cận này tương tự như null, nhưng sử dụng lớp Tùy chọn Java 8 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 một người gọi, tuy nhiên, người ta có thể thấy chữ ký đó quá dài dòng.

    Cập nhật: Java 8 bao gồm lớp java.util.Optionalngoài hộp, do đó không cần sử dụng ổi vì lý do cụ thể này trong Java 8. Mặc dù vậy, tên phương thức hơi khác một chút.

  5. Mô hình xây dựng. Mẫu trình xây dựng được sử dụng cho các nhà xây dựng và được triển khai 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ể truyền các đối số phương thức dưới dạng bản đồ 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 = (Integer)parameters.get("a");
        }
        if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 

    Trong Java 9, cách tiếp cận này trở nên dễ dàng hơn:

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

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


2
@Vitalii Fedorenko Hmm, tôi thiink bạn đã mắc một lỗi sao chép-dán-dán nhẹ ở # 6: Bạn đang kiểm tra kiểu cho một Integer mặc dù abiến của bạn là String (cast là chính xác).
Id không xác định

5
@Aetos liên kết của bạn đã chết.
Robino

2
@Robino liên kết thay thế lập luận rằng Tùy chọn không nên được sử dụng làm tham số ở đây . Mặc dù sử dụng Optionaltham số như một trong các tùy chọn, câu trả lời này là rất tốt.
Dherik

Giải pháp phổ biến nhất cho vấn đề trên có lẽ là quá tải phương thức. Mặc dù tôi chưa bao giờ thực sự là một fan hâm mộ của điều đó. Cá nhân tôi sẽ thích nếu họ thêm một số loại định danh cho các tham số cấu hình.
Alexander Heim

1
đây có lẽ là câu trả lời tốt nhất - bao gồm tất cả
xproph

104

Có các tham số tùy chọn với Java 5.0. Chỉ cần khai báo chức năng của bạn như thế này:

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

bạn có thể gọi với doSomething();hoặc doSomething(true);bây giờ.


13
Đây thực sự là câu trả lời chính xác. Nó đơn giản và nhỏ gọn. Chỉ cần nhớ rằng bạn có thể nhận được nhiều hơn một tham số để Java đặt chúng vào trong một mảng. Ví dụ: để truy xuất một tham số duy nhất, trước tiên bạn sẽ kiểm tra nội dung của mảng: 'code' boolean flag = (tùy chọnFlag.length <1)? False: optFlag [0];
Salvador Valencia

46
Không, đó không phải là câu trả lời đúng, bởi vì điều này không cho phép một tham số tùy chọn duy nhất, nhưng bất kỳ số lượng tham số tùy chọn. Mặc dù điều này gần với những gì OP muốn, nhưng nó không giống nhau. Đó là một nguồn tiềm năng của lỗi và hiểu lầm để chấp nhận nhiều tham số hơn bạn cần.
sleske

2
Cách này hoạt động nếu bạn chỉ có một tham số tùy chọn, nhưng bạn sẽ cần kiểm tra độ dài của mảng, v.v. để nó không siêu sạch: | Nếu bạn muốn tham số tùy chọn "một tham số" thì bạn cũng có thể chỉ cần khai báo hai phương thức (một phương thức bị quá tải doSomething() { doSomething(true); }không có mảng để xử lý, không có sự mơ hồ
rogerdpack

Nếu có nhiều tham số tùy chọn, điều này chỉ hoạt động nếu chúng đều có cùng kiểu dữ liệu. Chà, bạn có thể tạo ra loại Object, nhưng điều đó thực sự xấu xí.
Jay

Như @sleske đã lưu ý, sử dụng varargs có nhược điểm khủng khiếp là cho phép nhiều hơn một giá trị được thông qua. OP hỏi rõ ràng về một giải pháp cho "tùy chọn", có nghĩa là một giá trị hoặc bằng không. Làm thế nào điều này có thể được coi là một câu trả lời tốt bởi một số là do tôi.
Andreas Vogl

99

Bạn có thể sử dụng một cái gì đó như thế này:

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

Các paramsbiến là tùy chọn. Nó được coi là một mảng nullable của Object.

Kỳ lạ thay, tôi không thể tìm thấy bất cứ điều gì về điều này trong tài liệu, nhưng nó hoạt động!

Đây là "mới" trong Java 1.5 trở lên (không được hỗ trợ trong Java 1.4 trở về trước).

Tôi thấy người dùng bhoot đã đề cập đến điều này quá bên dưới.


55

Thật không may, Java không hỗ trợ các tham số mặc định trực tiếp.

Tuy nhiên, tôi đã viết một tập các chú thích JavaBean và một trong số chúng hỗ trợ các tham số mặc định như sau:

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

Bộ xử lý chú thích tạo ra quá tải phương thức để hỗ trợ chính xác điều này.

Xem http://code.google.com.vn/p/javadude/wiki/Annotations

Ví dụ đầy đủ tại http://code.google.com.vn/p/javadude/wiki/AnnotationsDefaultParameterExample


53

Không có tham số tùy chọn trong Java. Những gì bạn có thể làm là quá tải các hàm và sau đó chuyển các giá trị mặc định.

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

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

23

VarArss và quá tải đã được đề cập. Một tùy chọn khác là mẫu Builder, trông giống như thế này:

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

Mặc dù mẫu đó sẽ phù hợp nhất khi bạn cần các tham số tùy chọn trong hàm tạo.


tôi muốn thêm phụ thuộc tham số cho điều đó. Nói rằng tôi chỉ muốn đặt param3 nếu tôi đặt param1. Ví dụ. tôi chỉ muốn đặt thông báo tiến trình nếu tôi đặt tiến trình hiển thị. isProTHERVisible (). setProTHERMessage ("đang tải"). Làm thế nào tôi có thể đạt được điều đó?
Harshal Bhatt

14

Trong JDK> 1.5 bạn có thể sử dụng nó như thế này;

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

Bạn có thể làm điều bằng cách sử dụng phương thức nạp chồng như thế này.

 public void load(String name){ }

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

Ngoài ra, bạn có thể sử dụng chú thích @Nullable

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

chỉ cần truyền null làm tham số đầu tiên.

Nếu bạn đang chuyển biến cùng loại, bạn có thể sử dụng biến này

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

7

Phiên bản ngắn :

Sử dụng ba dấu chấm :

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

(dựa trên câu trả lời của @ VitaliiFedorenko)


2

Quá tải là tốt, nhưng nếu có nhiều biến cần giá trị mặc định, bạn sẽ kết thúc bằng:

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

Vì vậy, tôi sẽ đề nghị sử dụng Đối số biến do Java cung cấp. Đây là một liên kết để giải thích.


sử dụng varargs được coi là thực hành xấu. nếu hệ thống cần các phương thức như vậy (đã đề cập ở trên), thì bạn nên nghĩ đến một thiết kế mới vì thiết kế lớp trông tệ.
Diablo

2
Đó là cách thực hành thậm chí còn tồi tệ hơn để bù đắp cho những thiếu sót của Java và cáo buộc những người khác thực hành tồi vì đã nhận thấy những bất tiện đó và đưa ra cách giải quyết.
BrianO

1
Vui lòng thêm tất cả thông tin có liên quan vào câu trả lời của bạn thay vì liên kết với các nguồn bên ngoài - liên kết đã cho đã chết
Nico Haase

2

Bạn có thể sử dụng một lớp hoạt động giống như một trình xây dựng để chứa các giá trị tùy chọn của bạn như thế này.

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

Sử dụng như

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

Đầu ra là

someString = something, someInt = 5

Để bỏ qua tất cả các giá trị tùy chọn bạn phải gọi nó như thế nào foo(o -> {});hoặc nếu bạn thích, bạn có thể tạo một foo()phương thức thứ hai không lấy các tham số tùy chọn.

Sử dụng phương pháp này, bạn có thể chỉ định các giá trị tùy chọn theo bất kỳ thứ tự nào mà không có bất kỳ sự mơ hồ nào. Bạn cũng có thể có các tham số của các lớp khác nhau không giống như varargs. Cách tiếp cận này sẽ còn tốt hơn nữa nếu bạn có thể sử dụng các chú thích và tạo mã để tạo lớp Tùy chọn.


1

Java hiện hỗ trợ các tùy chọn trong 1.8, tôi bị mắc kẹt với lập trình trên Android vì vậy tôi đang sử dụng null cho đến khi tôi có thể cấu trúc lại mã để sử dụng các loại tùy chọn.

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

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

bạn có thể cung cấp ví dụ xin vui lòng?
sepehr

1

Đối số mặc định không thể được sử dụng trong Java. Trong C #, C ++ và Python, chúng ta có thể sử dụng chúng ..

Trong Java, chúng ta phải sử dụng 2 phương thức (hàm) thay vì một phương thức với các tham số mặc định.

Thí dụ:

Stash(int size); 

Stash(int size, int initQuantity);

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


28
Các phiên bản mới hơn của C # cho phép các tham số mặc định.
Sogger

2
java có tùy chọn "..." cho bất kỳ tham số nào.
Cacho Santa

0

Đây là một câu hỏi cũ có thể ngay cả trước khi loại Tùy chọn thực tế được giới thiệu nhưng ngày nay bạn có thể cân nhắc một số điều: - sử dụng phương thức nạp chồng - sử dụng Loại tùy chọn có lợi thế là tránh truyền NULL xung quanh Loại tùy chọn được giới thiệu trong Java 8 trước khi nó thường được sử dụng từ lib của bên thứ ba như Guava của Google. Sử dụng tùy chọn làm tham số / đối số có thể được coi là sử dụng quá mức vì mục đích chính là sử dụng nó làm thời gian trả về.

Tham chiếu: https://itcodehub.blogspot.com/2019/06/USE-optional-type-in-java.html


0

Chúng ta có thể tạo tham số tùy chọn bằng cách nạp chồng Phương thức hoặc Sử dụng DataType ...

| * | Phương thức nạp chồng:

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

Cách dễ nhất là

| * | DataType ... có thể là tham số tùy chọn

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

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


8
RetDtaTyp... nghiêm túc? Là RetDataTypequá khó để loại ra?
Alexander - Tái lập Monica

Bạn có thể thêm một số giải thích cho mã đó? Tất cả những biến lạ này làm gì?
Nico Haase

Thay đổi tên biến để dễ hiểu ...
Sujay UN

0

Nếu bạn đang dự định sử dụng một giao diện có nhiều tham số , người ta có thể sử dụng mẫu cấu trúc sau và áp dụng hoặc ghi đè - một phương pháp dựa trên yêu cầu của bạn .

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

0

Nếu đó là điểm cuối API, một cách trang nhã là sử dụng chú thích "Mùa xuân":

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

Lưu ý trong trường hợp này là InternalFunc sẽ yêu cầu biến và vì nó không phải là điểm cuối api, nên không thể sử dụng chú thích Mùa xuân này để làm cho nó tùy chọn. Tham khảo: https://www.baeldung.com/spring-request-param

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.