Opcjonalne parametry Java

812

Jak korzystać z opcjonalnych parametrów w Javie? Jaka specyfikacja obsługuje parametry opcjonalne?

Mike Pone
źródło

Odpowiedzi:

516

varargs może to zrobić (w pewien sposób). Poza tym należy podać wszystkie zmienne w deklaracji metody. Jeśli chcesz, aby zmienna była opcjonalna, możesz przeładować metodę za pomocą podpisu, który nie wymaga parametru.

private boolean defaultOptionalFlagValue = true;

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

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}
laginimaineb
źródło
Przeciążanie metod jest moim zdaniem dobrą odpowiedzią, podczas gdy varargs jest bardzo złą odpowiedzią. Usuń komentarz na temat varargs lub wyjaśnij poważną wadę wynikającą z możliwości zastosowania więcej niż jednej wartości zwrotu.
Andreas Vogl,
Dodanie zmiennych globalnych może powodować inne problemy.
Helen Cui
1652

Istnieje kilka sposobów symulacji opcjonalnych parametrów w Javie:

  1. Przeciążenie metody.

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

    Jednym z ograniczeń tego podejścia jest to, że nie działa, jeśli masz dwa opcjonalne parametry tego samego typu, a każdy z nich można pominąć.

  2. Varargs.

    a) Wszystkie parametry opcjonalne są tego samego typu:

    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) Rodzaje parametrów opcjonalnych mogą być różne:

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

    Główną wadą tego podejścia jest to, że jeśli parametry opcjonalne są różnych typów, tracisz sprawdzanie typu statycznego. Ponadto, jeśli każdy parametr ma inne znaczenie, potrzebujesz jakiegoś sposobu na ich rozróżnienie.

  3. Zero Aby rozwiązać ograniczenia poprzednich podejść, możesz zezwolić na wartości zerowe, a następnie przeanalizować każdy parametr w treści metody:

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

    Teraz należy podać wszystkie wartości argumentów, ale wartości domyślne mogą być zerowe.

  4. Klasa fakultatywna To podejście jest podobne do wartości zerowych, ale wykorzystuje klasę opcjonalną Java 8 dla parametrów o wartości domyślnej:

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

    Opcjonalne sprawia, że ​​metoda dzwoniąca jest jawna dla osoby dzwoniącej, jednak taki podpis może okazać się zbyt szczegółowy.

    Aktualizacja: Java 8 zawiera klasę java.util.Optionalgotową do użycia, więc nie ma potrzeby używania guava z tego konkretnego powodu w Javie 8. Nazwa metody jest jednak nieco inna.

  5. Wzór konstruktora. Wzorzec konstruktora jest używany dla konstruktorów i jest implementowany przez wprowadzenie oddzielnej klasy konstruktora:

     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. Mapy Gdy liczba parametrów jest zbyt duża i dla większości wartości domyślnych są zwykle używane, możesz przekazać argumenty metody jako mapę ich nazw / wartości:

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

    W Javie 9 takie podejście stało się łatwiejsze:

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

Pamiętaj, że możesz połączyć dowolne z tych podejść, aby osiągnąć pożądany rezultat.

Vitalii Fedorenko
źródło
2
@Vitalii Fedorenko Hmm, myślę, że popełniłeś niewielki błąd kopiuj-wklej -podczas nr 6: Sprawdzasz typ dla liczby całkowitej, chociaż twoja azmienna jest ciągiem (rzutowanie jest poprawne).
Nieznany identyfikator
5
@Atos twój link jest martwy.
Robino
2
@Robino alternatywa Link twierdząc, że opcjonalny nie powinien być stosowany jako parametr tutaj . Pomimo zastosowania Optionaljako parametru w jednej z opcji, ta odpowiedź jest bardzo dobra.
Dherik
Najczęstszym rozwiązaniem powyższego problemu jest prawdopodobnie przeciążenie metody. Chociaż tak naprawdę nigdy nie byłem tego fanem. Osobiście wolałbym, gdyby dodali jakiś identyfikator parametrów konfiguracyjnych.
Alexander Heim,
1
powinna to być prawdopodobnie najlepsza odpowiedź - obejmuje wszystko
xproph
104

Istnieją opcjonalne parametry w Javie 5.0. Po prostu zadeklaruj swoją funkcję w ten sposób:

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

możesz zadzwonić z doSomething();lub doSomething(true);teraz.

buc
źródło
13
To właściwie poprawna odpowiedź. To proste i kompaktowe. Pamiętaj tylko, że możesz uzyskać więcej niż jeden parametr, aby Java umieściła je w tablicy. Na przykład, aby pobrać pojedynczy parametr, najpierw sprawdź zawartość tablicy: „code” boolean flag = (opcjonalnyFlag.length <1)? False: opcjonalnyFlag [0];
Salvador Valencia,
46
Nie, to nie jest poprawna odpowiedź, ponieważ nie pozwala to na pojedynczy parametr opcjonalny, ale na dowolną liczbę parametrów opcjonalnych. Chociaż jest to bliskie temu, czego chce OP, to nie jest to samo. Potencjalnym źródłem błędów i nieporozumień jest zaakceptowanie większej liczby parametrów niż potrzebujesz.
śleske
2
Ten sposób działa, jeśli masz tylko jeden opcjonalny parametr, ale musisz sprawdzić długość tablicy itp., Więc nie jest to super czyste: | Jeśli chcesz opcjonalnego parametru „jeden parametr”, możesz równie dobrze zadeklarować dwie metody (jedna przeciążona doSomething() { doSomething(true); }nie ma tablic do czynienia, nie ma dwuznaczności
rogerdpack
Jeśli istnieje wiele opcjonalnych parametrów, działa to tylko wtedy, gdy wszystkie są tego samego typu danych. Możesz stworzyć typ Object, ale robi się to naprawdę brzydkie.
Jay
Jak zauważył @sleske, używanie varargs ma straszną wadę polegającą na tym, że pozwala na przekazanie więcej niż jednej wartości. OP wyraźnie zapytał o rozwiązanie dla „opcjonalnego”, co oznacza albo jedną wartość, albo zero. Jak niektórzy uważają to za dobrą odpowiedź, to mnie omija.
Andreas Vogl,
99

Możesz użyć czegoś takiego:

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

paramsZmienna jest opcjonalna. Jest traktowany jako zerowalna tablica obiektów.

O dziwo, nie mogłem nic na ten temat znaleźć w dokumentacji, ale działa!

Jest to „nowe” w Javie 1.5 i nowszych wersjach (nie obsługiwane w Javie 1.4 lub wcześniejszych).

Widzę, że użytkownik bhoot wspomniał o tym również poniżej.

theninjagreg
źródło
55

Niestety Java nie obsługuje bezpośrednio parametrów domyślnych.

Jednak napisałem zestaw adnotacji JavaBean, a jeden z nich obsługuje domyślne parametry, takie jak:

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

Procesor adnotacji generuje przeciążenia metody, aby odpowiednio to obsługiwać.

Zobacz http://code.google.com/p/javadude/wiki/Annotacje

Pełny przykład na http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample

Scott Stanchfield
źródło
53

W Javie nie ma opcjonalnych parametrów. Możesz przeciążyć funkcje, a następnie przekazać wartości domyślne.

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

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}
Dario
źródło
23

Wspomniano o VarArgs i przeciążeniu. Inną opcją jest wzorzec konstruktora, który wyglądałby mniej więcej tak:

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

Chociaż ten wzorzec byłby najbardziej odpowiedni, gdy potrzebujesz opcjonalnych parametrów w konstruktorze.

Yishai
źródło
Chcę dodać do tego zależność parametrów. powiedzmy, że chcę ustawić param3 tylko, jeśli ustawię param1. Na przykład Chcę ustawić komunikat o postępie tylko wtedy, gdy ustawiam widoczny postęp. isProgressVisible (). setProgressMessage („ładowanie”). jak mogę to osiągnąć?
Harshal Bhatt
14

W JDK> 1.5 możesz używać tego w ten sposób;

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);
        }
    }
}
az3
źródło
7

Możesz to zrobić za pomocą metody przeciążania w ten sposób.

 public void load(String name){ }

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

Możesz także użyć adnotacji @Nullable

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

wystarczy przekazać null jako pierwszy parametr.

Jeśli przekazujesz zmienną tego samego typu, możesz jej użyć

public void load(String name...){}
Ishan Fernando
źródło
7

Krótka wersja :

Za pomocą trzech kropek :

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

(na podstawie odpowiedzi @ VitaliiFedorenko)

T.Todua
źródło
2

Przeładowanie jest w porządku, ale jeśli istnieje wiele zmiennych, które wymagają wartości domyślnej, otrzymasz:

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

Sugerowałbym więc użycie argumentu zmiennych dostarczanego przez Javę. Oto link do wyjaśnienia.

Jigar Shah
źródło
stosowanie varargs jest uważane za złą praktykę. jeśli system potrzebuje takich (wyżej wymienionych) metod, powinieneś pomyśleć o nowym projekcie, ponieważ projekt klasy wygląda źle.
Diablo,
2
Jeszcze gorszą praktyką jest wymyślanie usprawiedliwień dla braków Java i oskarżanie innych o złe praktyki za zauważanie tych niedogodności i wymyślanie obejść.
BrianO
1
Dodaj wszystkie istotne informacje do swojej odpowiedzi zamiast linku do źródeł zewnętrznych - podany link jest martwy
Nico Haase
2

Możesz użyć klasy, która działa podobnie jak konstruktor, aby zawierać opcjonalne wartości w ten sposób.

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

Użyj jak

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

Dane wyjściowe to

someString = something, someInt = 5

Aby pominąć wszystkie opcjonalne wartości, które trzeba by tak nazwać foo(o -> {});lub, jeśli wolisz, możesz utworzyć drugą foo()metodę, która nie przyjmuje parametrów opcjonalnych.

Korzystając z tego podejścia, możesz określić opcjonalne wartości w dowolnej kolejności bez żadnych dwuznaczności. Możesz także mieć parametry różnych klas w przeciwieństwie do varargs. Takie podejście byłoby jeszcze lepsze, gdyby można użyć adnotacji i generowania kodu do utworzenia klasy Options.

rybai
źródło
1

Java obsługuje teraz opcjonalne wersje 1.8, utknąłem w programowaniu na Androidzie, więc używam zer, dopóki nie będę mógł przefakturować kodu, aby użyć opcjonalnych typów.

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

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}
Śrut
źródło
czy możesz podać przykład?
sepehr
1

Domyślnych argumentów nie można używać w Javie. Gdzie w C #, C ++ i Python, możemy ich użyć ..

W Javie musimy użyć 2 metod (funkcji) zamiast jednej z domyślnymi parametrami.

Przykład:

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-

Parvinder Singh
źródło
28
Nowsze wersje C # pozwalają na domyślne parametry.
Sogger,
2
java ma również opcję „...” dla dowolnego parametru.
Cacho Santa
0

Jest to stare pytanie, być może jeszcze przed wprowadzeniem rzeczywistego typu Opcjonalnego, ale obecnie można rozważyć kilka rzeczy: - użycie przeciążenia metody - użycie Typu opcjonalnego, który ma tę zaletę, że pozwala uniknąć przepuszczania wartości NULL w okolicy Opcjonalny typ został wprowadzony w Javie 8 przed jego użyciem z lib stron trzecich, takich jak Google Guava. Używanie opcjonalnych jako parametrów / argumentów można uznać za nadmierne wykorzystanie, ponieważ głównym celem było wykorzystanie go jako czasu powrotu.

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

xproph
źródło
0

Możemy ustawić opcjonalny parametr przez przeciążenie metody lub użycie DataType ...

| * | Przeciążenie metody:

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

Najłatwiej jest

| * | Typ danych ... może być parametrem opcjonalnym

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

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

Sujay ONZ
źródło
8
RetDtaTyp... poważnie? Czy RetDataTypezbyt trudno jest napisać?
Alexander - Przywróć Monikę
Czy możesz dodać więcej wyjaśnień do tego kodu? Co robią te wszystkie dziwne zmienne?
Nico Haase
Zmieniono nazwy zmiennych dla łatwiejszego zrozumienia ...
Sujay ONZ,
0

Jeśli planujesz użyć interfejsu z wieloma parametrami , możesz użyć następującego wzoru strukturalnego i zastosować lub zastąpić - metodę opartą na twoich wymaganiach .

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}
Mukundhan
źródło
0

Jeśli jest to punkt końcowy interfejsu API, eleganckim sposobem jest użycie adnotacji „Spring”:

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

Zauważ, że w tym przypadku innerFunc będzie wymagało zmiennej, a ponieważ nie jest to punkt końcowy interfejsu API, nie można użyć tej adnotacji z wiosny, aby uczynić ją opcjonalną. Odniesienie: https://www.baeldung.com/spring-request-param

Sophie Cooperman
źródło