it-swarm.com.de

Unterstützt Java Standardparameterwerte?

Ich bin auf Java Code gestoßen, der die folgende Struktur hatte:

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

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

Ich weiß, dass ich in C++ einem Parameter einen Standardwert zuweisen kann. Zum Beispiel:

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

Unterstützt Java diese Art von Syntax? Gibt es Gründe, warum diese zweistufige Syntax vorzuziehen ist?

1524
gnavi

Nein, die Struktur, die Sie gefunden haben, ist wie Java damit umgeht (dh mit Überladung anstelle von Standardparametern).

Für Konstruktoren Siehe Handbuch zu Java: Programming Language Tipp 1 (statische Factory-Methoden anstelle von Konstruktoren berücksichtigen) bei Überladung wird immer komplizierter. Bei anderen Methoden kann es hilfreich sein, einige Fälle umzubenennen oder ein Parameterobjekt zu verwenden. In diesem Fall ist die Komplexität so hoch, dass eine Unterscheidung schwierig ist. Ein konkreter Fall ist, dass Sie nach der Reihenfolge der Parameter unterscheiden müssen, nicht nur nach Nummer und Typ.

873
Kathy Van Stone

Nein, aber Sie können das Builder-Muster verwenden, wie in diese Stapelüberlauf-Antwort beschrieben.

Wie in der verknüpften Antwort beschrieben, können Sie mit dem Builder-Muster Code wie folgt schreiben

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

in denen einige Felder Standardwerte haben oder auf andere Weise optional sein können.

604
Eli Courtwright

Es gibt verschiedene Möglichkeiten, Standardparameter 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 Sie die statische Typprüfung verlieren, wenn optionale Parameter unterschiedlichen Typs sind. Wenn außerdem jeder Parameter eine andere Bedeutung hat, müssen Sie sie auf irgendeine Weise unterscheiden.

  3. Nulls. Um die Einschränkungen der vorherigen Ansätze zu umgehen, 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 Java 8 Optionale Klasse 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 Aufrufer explizit angegeben, eine solche Signatur ist jedoch möglicherweise zu ausführlich.

  5. Builder-Muster. Das Builder-Muster wird für Konstruktoren verwendet und durch die 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. Maps. Wenn die Anzahl der Parameter zu groß ist und für die meisten von ihnen Standardwerte verwendet werden, können Sie Methodenargumente als Map 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 = (String)parameters.get("a");
        } else if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>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.

430

Traurigerweise Nein.

234
Rob H

Leider ja.

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

könnte in Java 1.5 geschrieben werden als:

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

Aber ob Sie dies tun oder nicht, hängt davon ab, wie Sie den Compiler generieren

new Boolean[]{}

für jeden Anruf.

Für mehrere voreingestellte Parameter:

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

könnte in Java 1.5 geschrieben werden als:

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

Dies entspricht der C++ - Syntax, die nur Standardparameter am Ende der Parameterliste zulässt.

Über die Syntax hinaus gibt es einen Unterschied, bei dem die Überprüfung des Laufzeit-Typs auf übergebene standardmäßige Parameter und der C++ - Typ diese während der Kompilierung überprüft.

80
ebelisle

Nein, aber Sie können sie sehr leicht emulieren. Was in C++ war:

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

In Java wird es eine überladene Funktion sein:

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

Zuvor wurde erwähnt, dass Standardparameter zu mehrdeutigen Fällen bei der Überladung von Funktionen führen. Das ist einfach nicht wahr, wir können im Fall von C++ sehen: Ja, vielleicht kann es zu mehrdeutigen Fällen kommen, aber dieses Problem kann leicht gelöst werden. Es wurde einfach nicht in Java entwickelt, wahrscheinlich weil die Entwickler eine viel einfachere Sprache wollten als C++ - wenn sie Recht hatten, ist das eine andere Frage. Aber die meisten von uns glauben nicht, dass er Java wegen seiner Einfachheit verwendet.

36
peterh

Sie können dies in Scala tun, das auf der JVM ausgeführt wird und mit Java Programmen kompatibel ist. http://www.scala-lang.org/

d.h.

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

Nein, aber der einfachste Weg dies zu implementieren ist:

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

oder anstelle des ternären Operators können Sie if verwenden:

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}
12
simhumileco

Ich sage hier vielleicht das Offensichtliche, aber warum nicht einfach den "Standard" -Parameter selbst implementieren?

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

für die Standardeinstellung würden Sie ether verwenden

func ("mein String");

und wenn Sie die Standardeinstellung nicht verwenden möchten, verwenden Sie

func ("my string", false);

11
IronWolf

Nein. Im Allgemeinen hat Java nicht viel (irgendeinen) syntaktischen Zucker, da sie versucht haben, eine einfache Sprache zu erstellen.

6
tomjen

Wie Scala erwähnt wurde, ist Kotlin ebenfalls erwähnenswert. In Kotlin können Funktionsparameter auch Standardwerte haben und sich sogar auf andere Parameter beziehen:

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

Kotlin läuft wie Scala auf der JVM und kann problemlos in bestehende Java Projekte integriert werden.

6
mrts

Nein.

Sie können dasselbe Verhalten erzielen, indem Sie ein Objekt mit intelligenten Standardwerten übergeben. Aber auch hier kommt es darauf an, welchen Fall Sie gerade haben.

6
Santosh Gokak

Es wird nicht unterstützt, aber es gibt verschiedene Optionen wie die Verwendung des Parameterobjektmusters mit etwas Syntaxzucker:

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

In diesem Beispiel erstellen wir ParameterObject mit Standardwerten und überschreiben sie im Abschnitt zur Initialisierung von Klasseninstanzen { param1 = 10; param2 = "bar";}

4
hoaz

Versuchen Sie diese Lösung:

public int getScore(int score, Integer... bonus)
{
    if(bonus.length > 0)
    {
        return score + bonus[0];
    }

    return score;
}
3
Hamzeh Soboh

Sie können Java Method Invocation Builder verwenden, um den Builder automatisch mit Standardwerten zu generieren.

Fügen Sie einfach @GenerateMethodInvocationBuilder zur Klasse oder Schnittstelle hinzu und @Default zu Parametern in Methoden, für die Standardwerte gewünscht werden. Zur Kompilierungszeit wird ein Builder mit den Standardwerten generiert, die Sie in Ihren Anmerkungen angegeben haben.

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

Und dann können Sie die Methoden aufrufen.

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

Oder setzen Sie einen der Standardwerte auf einen anderen Wert.

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

Ein ähnlicher Ansatz zu https://stackoverflow.com/a/13864910/2323964 , der in Java 8 funktioniert, ist die Verwendung einer Schnittstelle mit Standard-Gettern. Dies ist mehr Whitespace-Wortschatz, aber spottbar, und es ist großartig, wenn Sie eine Reihe von Fällen haben, in denen Sie die Aufmerksamkeit auf die Parameter lenken möchten.

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
Novaterata

Ich habe jetzt einige Zeit damit verbracht, herauszufinden, wie dies mit Methoden verwendet werden kann, die Werte zurückgeben, und ich habe bisher keine Beispiele gesehen. Ich dachte, es könnte nützlich sein, dies hier hinzuzufügen:

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

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

Es gibt ein halbes Dutzend oder bessere Probleme wie dieses, irgendwann kommt man zum statischen Fabrikmuster ... siehe dazu die Krypto-API. Schwierig zu erklärende Sortierung, aber denken Sie daran: Wenn Sie einen Konstruktor haben, entweder einen Standardwert oder einen anderen, besteht die einzige Möglichkeit, den Status über die geschweiften Klammern hinaus zu verbreiten, darin, einen Booleschen Wert isValid zu haben. (zusammen mit dem Konstruktor null als Standardwert v failed) oder werfen Sie eine Ausnahme, die niemals informativ ist, wenn Sie sie von Feldbenutzern zurückerhalten.

Code Richtig, verdammt, ich schreibe tausend Linienkonstruktoren und mache, was ich brauche. Ich verwende isValid bei der Objektkonstruktion - also zwei Linienkonstruktoren -, aber aus irgendeinem Grund migriere ich zum statischen Factory-Muster. Ich scheine nur, dass Sie viel tun können, wenn Sie in einem Methodenaufruf sind, es gibt immer noch sync () -Probleme, aber die Standardeinstellungen können besser (sicherer) ersetzt werden.

Ich denke, was wir hier tun müssen, ist das Problem von null als Standardwert gegenüber etwas String one = new String ("") anzugehen; Führen Sie als Member-Variable eine Überprüfung auf Null durch, bevor Sie eine an den Konstruktor übergebene Zeichenfolge zuweisen.

Sehr bemerkenswert ist die Menge an roher, stratosphärischer Informatik in Java.

C++ und so weiter haben Hersteller-Bibliotheken, ja. Java kann sie auf großen Servern überholen, da es sich um eine riesige Toolbox handelt. Studieren Sie statische Initialisierungsblöcke und bleiben Sie bei uns.

1
Nicholas Jordan

So habe ich es gemacht ... es ist vielleicht nicht so praktisch, als hätte man ein 'optionales Argument' für Ihren definierten Parameter, aber es erledigt die Aufgabe:

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

Beachten Sie, dass ich denselben Methodennamen entweder nur mit einer Zeichenfolge oder mit einer Zeichenfolge und einem booleschen Wert aufrufen kann. In diesem Fall wird durch Setzen von wipeClean auf true der gesamte Text in meinem TextArea durch die angegebene Zeichenfolge ersetzt. Wenn Sie wipeClean auf false setzen oder alles weglassen, wird der bereitgestellte Text einfach an die TextArea angehängt.

Beachten Sie auch, dass ich in den beiden Methoden keinen Code wiederhole, sondern lediglich die Funktionalität hinzufüge, die TextArea zurücksetzen zu können, indem eine neue Methode mit demselben Namen nur mit dem hinzugefügten Booleschen Wert erstellt wird.

Ich denke tatsächlich, dass dies ein wenig sauberer ist, als wenn Java ein 'optionales Argument' für unsere Parameter bereitgestellt hätte, da wir dann Code für Standardwerte usw. benötigen würden. In diesem Beispiel muss ich mir keine Sorgen machen irgendetwas davon. Ja, ich habe meiner Klasse eine weitere Methode hinzugefügt, aber meiner bescheidenen Meinung nach ist es auf lange Sicht einfacher zu lesen.

1
Michael Sims

NEIN, aber wir haben eine Alternative in Form von Funktionsüberladung.

wird aufgerufen, wenn kein Parameter übergeben wurde

void operation(){

int a = 0;
int b = 0;

} 

wird aufgerufen, wenn ein Parameter übergeben wurde

void operation(int a){

int b = 0;
//code

} 

wird aufgerufen, wenn Parameter b übergeben wird

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