Optionale Java-Parameter

812

Wie verwende ich optionale Parameter in Java? Welche Spezifikation unterstützt optionale Parameter?

Mike Pone
quelle

Antworten:

516

varargs könnte das (in gewisser Weise) tun. Ansonsten müssen alle Variablen in der Deklaration der Methode angegeben werden. Wenn eine Variable optional sein soll, können Sie die Methode mit einer Signatur überladen, für die der Parameter nicht erforderlich ist.

private boolean defaultOptionalFlagValue = true;

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

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}
Laginimaineb
quelle
Methodenüberladung ist meiner Meinung nach eine gute Antwort, während Varargs eine sehr schlechte Antwort ist. Bitte entfernen Sie entweder den Kommentar zu varargs oder erläutern Sie den schwerwiegenden Nachteil, der durch die Möglichkeit von mehr als einem Rückgabewert verursacht wird.
Andreas Vogl
Das Hinzufügen globaler Variablen kann einige andere Probleme verursachen.
Helen Cui
1652

Es gibt verschiedene Möglichkeiten, optionale Parameter in Java zu simulieren:

  1. Methodenüberladung.

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

    Eine der Einschränkungen dieses Ansatzes besteht darin, dass er nicht funktioniert, wenn Sie zwei optionale Parameter desselben Typs haben und jeder von ihnen weggelassen werden kann.

  2. Varargs.

    a) Alle optionalen Parameter sind vom gleichen Typ:

    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) Die Arten der optionalen Parameter können unterschiedlich sein:

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

    Der Hauptnachteil dieses Ansatzes besteht darin, dass bei statischen Parametern unterschiedlicher Typen die statische Typprüfung verloren geht. Wenn jeder Parameter eine andere Bedeutung hat, müssen Sie ihn auf irgendeine Weise unterscheiden.

  3. Nullen. Um die Einschränkungen der vorherigen Ansätze zu beseitigen, können Sie Nullwerte zulassen und dann jeden Parameter in einem Methodenkörper analysieren:

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

    Jetzt müssen alle Argumentwerte angegeben werden, die Standardwerte können jedoch null sein.

  4. Optionale Klasse. Dieser Ansatz ähnelt Nullen, verwendet jedoch die optionale Klasse Java 8 für Parameter mit einem Standardwert:

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

    Optional wird ein Methodenvertrag für einen Anrufer explizit festgelegt. Eine solche Signatur ist jedoch möglicherweise zu ausführlich.

    Update: Java 8 enthält die sofort einsatzbereite Klasse java.util.Optional, sodass in Java 8 aus diesem speziellen Grund keine Guave verwendet werden muss. Der Methodenname ist jedoch etwas anders.

  5. Builder-Muster. Das Builder-Muster wird für Konstruktoren verwendet und durch Einführung einer separaten Builder-Klasse implementiert:

     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. Karten. Wenn die Anzahl der Parameter zu groß ist und für die meisten Standardwerte normalerweise verwendet wird, können Sie Methodenargumente als Zuordnung ihrer Namen / Werte übergeben:

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

    In Java 9 wurde dieser Ansatz einfacher:

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

Bitte beachten Sie, dass Sie jeden dieser Ansätze kombinieren können, um ein wünschenswertes Ergebnis zu erzielen.

Vitalii Fedorenko
quelle
2
@Vitalii Fedorenko Hmm, ich denke, Sie haben in # 6 einen kleinen Fehler beim Kopieren und Einfügen gemacht: Sie suchen nach einer Ganzzahl, obwohl Ihre aVariable ein String ist (Cast ist korrekt).
Unbekannte
5
@Aetos dein Link ist tot.
Robino
2
@Robino alternativer Link mit der Begründung, dass Optional hier nicht als Parameter verwendet werden sollte . Trotz der Verwendung Optionalals Parameter in einer der Optionen ist diese Antwort sehr gut.
Dherik
Die häufigste Lösung für das oben genannte Problem ist wahrscheinlich das Überladen von Methoden. Obwohl ich nie wirklich ein Fan davon war. Persönlich würde ich es vorziehen, wenn sie eine Art Kennung für Konfigurationsparameter hinzufügen würden.
Alexander Heim
1
Dies sollte wahrscheinlich die beste Antwort sein - deckt alle ab
xproph
104

In Java 5.0 gibt es optionale Parameter. Deklarieren Sie einfach Ihre Funktion wie folgt:

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

Sie könnten mit doSomething();oder doSomething(true);jetzt anrufen .

bhoot
quelle
13
Dies ist eigentlich die richtige Antwort. Es ist einfach und kompakt. Denken Sie daran, dass Sie mehr als einen Parameter erhalten können, damit Java sie in ein Array einfügt. Um beispielsweise einen einzelnen Parameter abzurufen, überprüfen Sie zuerst den Inhalt des Arrays: 'code' boolean flag = (optionalFlag.length <1)? False: optionalFlag [0];
Salvador Valencia
46
Nein, dies ist nicht die richtige Antwort, da hierfür kein einziger optionaler Parameter, sondern eine beliebige Anzahl optionaler Parameter zulässig ist. Dies entspricht zwar in etwa den Wünschen von OP, ist jedoch nicht dasselbe. Es ist eine potenzielle Quelle für Fehler und Missverständnisse, mehr Parameter zu akzeptieren, als Sie benötigen.
Sleske
2
Diese Methode funktioniert, wenn Sie nur einen optionalen Parameter haben, aber die Länge des Arrays usw. überprüfen müssen, damit es nicht besonders sauber ist: | Wenn Sie den optionalen Parameter "ein Parameter" möchten, können Sie auch einfach zwei Methoden deklarieren (eine überladen, doSomething() { doSomething(true); }keine Arrays, keine Mehrdeutigkeit
Rogerdpack
Wenn mehrere optionale Parameter vorhanden sind, funktioniert dies nur, wenn alle vom gleichen Datentyp sind. Nun, Sie könnten den Typ Object erstellen, aber das wird wirklich hässlich.
Jay
Wie @sleske bemerkte, hat die Verwendung von varargs den schrecklichen Nachteil, dass mehr als ein Wert übergeben werden kann. OP fragte eindeutig nach einer Lösung für "optional", was entweder einen Wert oder Null bedeutet. Wie dies von einigen als gute Antwort angesehen werden kann, ist mir ein Rätsel.
Andreas Vogl
99

Sie können so etwas verwenden:

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

Die paramsVariable ist optional. Es wird als nullbares Array von Objekten behandelt.

Seltsamerweise konnte ich in der Dokumentation nichts dazu finden, aber es funktioniert!

Dies ist "neu" in Java 1.5 und höher (wird in Java 1.4 oder früher nicht unterstützt).

Ich sehe, dass Benutzer bhoot dies auch unten erwähnt hat.

theninjagreg
quelle
55

Leider unterstützt Java Standardparameter nicht direkt.

Ich habe jedoch eine Reihe von JavaBean-Anmerkungen geschrieben, von denen eine Standardparameter wie die folgenden unterstützt:

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

Der Anmerkungsprozessor generiert die Methodenüberladungen, um dies ordnungsgemäß zu unterstützen.

Siehe http://code.google.com/p/javadude/wiki/Annotations

Vollständiges Beispiel unter http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample

Scott Stanchfield
quelle
53

In Java gibt es keine optionalen Parameter. Sie können die Funktionen überladen und dann Standardwerte übergeben.

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

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

VarArgs und Überladung wurden erwähnt. Eine andere Option ist ein Builder-Muster, das ungefähr so ​​aussehen würde:

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

Obwohl dieses Muster am besten geeignet ist, wenn Sie optionale Parameter in einem Konstruktor benötigen.

Yishai
quelle
Ich möchte eine Parameterabhängigkeit für das hinzufügen. Sagen wir, ich möchte nur param3 setzen, wenn ich param1 setze. Zum Beispiel. Ich möchte die Fortschrittsmeldung nur einstellen, wenn ich den Fortschritt sichtbar mache. isProgressVisible (). setProgressMessage ("Laden"). Wie kann ich das erreichen?
Harshal Bhatt
14

In JDK> 1.5 können Sie es so verwenden;

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
quelle
7

Sie können dies mit einer solchen Methodenüberladung tun.

 public void load(String name){ }

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

Sie können auch die Annullation @Nullable verwenden

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

Übergeben Sie einfach null als ersten Parameter.

Wenn Sie dieselbe Typvariable übergeben, können Sie diese verwenden

public void load(String name...){}
Ishan Fernando
quelle
7

Kurzfassung:

Mit drei Punkten :

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

(basierend auf der Antwort von @ VitaliiFedorenko)

T.Todua
quelle
2

Überladen ist in Ordnung, aber wenn es viele Variablen gibt, die einen Standardwert benötigen, erhalten Sie Folgendes:

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

Daher würde ich vorschlagen, das von Java bereitgestellte Variablenargument zu verwenden. Hier ist ein Link zur Erklärung.

Jigar Shah
quelle
Die Verwendung von Varargs wird als schlechte Praxis angesehen. Wenn das System solche (oben genannten) Methoden benötigt, sollten Sie sich ein neues Design vorstellen, da das Klassendesign schlecht aussieht.
Diablo
2
Es ist noch schlimmer, Entschuldigungen für Java-Mängel zu finden und andere der schlechten Praxis zu beschuldigen, diese Unannehmlichkeiten bemerkt und Problemumgehungen entwickelt zu haben.
BrianO
1
Bitte fügen Sie alle relevanten Informationen zu Ihrer Antwort hinzu, anstatt auf externe Quellen zu verlinken - der angegebene Link ist tot
Nico Haase
2

Sie können eine Klasse verwenden, die ähnlich wie ein Builder funktioniert, um Ihre optionalen Werte wie diese zu enthalten.

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

Verwenden Sie wie

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

Ausgabe ist

someString = something, someInt = 5

Um alle optionalen Werte zu überspringen, die Sie wie folgt aufrufen foo(o -> {});möchten, oder wenn Sie dies bevorzugen, können Sie eine zweite foo()Methode erstellen , die die optionalen Parameter nicht verwendet.

Mit diesem Ansatz können Sie optionale Werte in beliebiger Reihenfolge ohne Mehrdeutigkeit angeben. Im Gegensatz zu varargs können Sie auch Parameter verschiedener Klassen verwenden. Dieser Ansatz wäre sogar noch besser, wenn Sie zum Erstellen der Options-Klasse Anmerkungen und Codegenerierung verwenden können.

Rybai
quelle
1

Java unterstützt jetzt Optionals in 1.8. Ich bin mit der Programmierung auf Android festgefahren, daher verwende ich Nullen, bis ich den Code umgestalten kann, um optionale Typen zu verwenden.

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

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}
Pellet
quelle
Können Sie bitte ein Beispiel geben?
September
1

Standardargumente können in Java nicht verwendet werden. Wo in C #, C ++ und Python können wir sie verwenden.

In Java müssen wir 2 Methoden (Funktionen) anstelle einer mit Standardparametern verwenden.

Beispiel:

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
quelle
28
Neuere Versionen von C # erlauben Standardparameter.
Sogger
2
Java hat die Option "..." auch für jeden Parameter.
Cacho Santa
0

Dies ist eine alte Frage, vielleicht noch bevor der eigentliche optionale Typ eingeführt wurde, aber heutzutage können Sie einige Dinge berücksichtigen: - Überladen von Methoden verwenden - Optionalen Typ verwenden, der den Vorteil hat, dass NULL-Werte nicht um den optionalen Typ herum übergeben werden, wurde in Java 8 eingeführt, bevor er normalerweise verwendet wurde von Drittanbietern wie Googles Guava. Die Verwendung von optional als Parameter / Argumente kann als Überbeanspruchung angesehen werden, da der Hauptzweck darin bestand, sie als Rückgabezeit zu verwenden.

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

xproph
quelle
0

Wir können optionale Parameter durch Überladen von Methoden oder Verwenden von DataType festlegen ...

| * | Methodenüberladung:

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

Der einfachste Weg ist

| * | DataType ... kann ein optionaler Parameter sein

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

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

Sujay UN
quelle
8
RetDtaTyp... Ernsthaft? Ist RetDataTypezu schwer zu tippen?
Alexander - Reinstate Monica
Können Sie diesem Code weitere Erklärungen hinzufügen? Was machen all diese seltsamen Variablen?
Nico Haase
Die Variablennamen wurden geändert, um das Verständnis zu erleichtern ...
Sujay UN
0

Wenn Sie eine Schnittstelle mit mehreren Parametern verwenden möchten , können Sie das folgende Strukturmuster verwenden und apply implementieren oder überschreiben - eine Methode, die auf Ihren Anforderungen basiert .

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

Wenn es sich um einen API-Endpunkt handelt, können Sie auf elegante Weise "Spring" -Anmerkungen verwenden:

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

Beachten Sie in diesem Fall, dass für innerFunc die Variable erforderlich ist. Da es sich nicht um einen API-Endpunkt handelt, kann diese Spring-Annotation nicht verwendet werden, um sie optional zu machen. Referenz: https://www.baeldung.com/spring-request-param

Sophie Cooperman
quelle