Domanda

Mi sono imbattuto in un po 'di codice Java che ha avuto la seguente struttura:

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

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

So che in C ++ posso assegnare un parametro di un valore predefinito. Ad esempio:

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

Fa supporto Java questo tipo di sintassi? Ci sono delle ragioni per le quali questa sintassi in due fasi è preferibile?

È stato utile?

Soluzione

No, la struttura che avete trovato è come Java gestisce (cioè con sovraccarico invece di parametri di default).

Per i costruttori, Vedi Effective Java: Programmazione del Language Guide Articolo 1 punta (Considerare metodi factory statici invece dei costruttori) se il sovraccarico è sempre complicato. Per altri metodi, rinominare alcuni casi o utilizzando un oggetto parametro può aiutare. Questo è quando si dispone di sufficiente complessità che differenziazione è difficile. Un caso certo è dove si deve differenziare con l'ordine dei parametri, non solo il numero e il tipo.

Altri suggerimenti

No, ma è possibile utilizzare il Builder modello , come descritto in this Stack Overflow risposta .

Come descritto nella risposta collegato, il builder permette di scrivere codice come

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

in cui alcuni campi possono avere valori di default o comunque essere facoltativa.

Ci sono diversi modi per simulare i parametri predefiniti in Java:

  1. Metodo di sovraccarico.

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

    Uno dei limiti di questo approccio è che non funziona se si dispone di due parametri opzionali dello stesso tipo e nessuno di essi può essere omesso.

  2. varargs.

    a) Tutti i parametri opzionali sono dello stesso tipo:

    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) Tipi di parametri opzionali possono essere diversi:

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

    L'inconveniente principale di questo approccio è che se i parametri opzionali sono di diversi tipi si perde il controllo di tipo statico. Inoltre, se ogni parametro ha significato diverso è necessario un modo per distinguerli.

  3. Null Per affrontare le limitazioni degli approcci precedenti si può permettere valori nulli e quindi analizzare ogni parametro in un corpo del metodo:.

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

    Ora tutti gli argomenti devono essere muniti valori, ma quelli di default possono essere nulli.

  4. class opzionale Questo approccio è simile a valori nulli, ma utilizza Java 8 classe opzionale per i parametri che hanno un valore di default:.

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

    opzionale rende un contratto metodo esplicito per un chiamante, tuttavia, si può trovare tale firma troppo prolisso.

  5. builder Il builder viene utilizzato per i costruttori ed è implementata con l'introduzione di una classe separata Costruttore:.

     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 Quando il numero di parametri è troppo grande e per la maggior parte di loro valori di default di solito vengono utilizzati, è possibile passare argomenti del metodo come una mappa dei loro nomi / valori:.

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

Si prega di notare che è possibile combinare qualsiasi di questi approcci per ottenere un risultato desiderabile.

Purtroppo, no.

Purtroppo, sì.

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

potrebbe essere scritto in Java 1.5 come:

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

Ma se o non si deve dipendere da come ti senti per il compilatore genera un

new Boolean[]{}

per ogni chiamata.

Per molteplici parametri alle insolvenze:

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

potrebbe essere scritto in Java 1.5 come:

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

Questo corrisponde sintassi C ++, che consente solo i parametri di default alla fine dell'elenco dei parametri.

Al di là della sintassi, v'è una differenza in cui questo tipo ha eseguito il controllo del tempo per i parametri passati alle insolvenze e C ++ di tipo li controlla durante la compilazione.

No, ma si può facilmente emularli. Ciò che in C ++ è stato:

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

In Java, sarà una funzione di sovraccarico:

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

In precedenza è stato detto, che i parametri di default causati casi ambigui in funzione di sovraccarico. Questo semplicemente non è vero, possiamo vedere nel caso del C ++: sì, forse è in grado di creare casi ambigui, ma questi problema può essere facilmente gestito. E 'semplicemente non è stato sviluppato in Java, probabilmente perché i creatori volevano un linguaggio molto più semplice così come C ++ - se avessero ragione, è un'altra questione. Ma la maggior parte di noi non pensa che usa Java per la sua semplicità.

È possibile fare questo è a Scala, che gira su JVM ed è compatibile con i programmi Java. http://www.scala-lang.org/

cioè.

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

No , ma il modo più semplice per implementare questo è:

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

o al posto di operatore ternario , è possibile utilizzare if:

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

Potrei affermare l'ovvio qui, ma perché non è sufficiente implementare il parametro "default" te stesso?

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

per il default si etere uso

func ( "la mia stringa");

e se non si desidera utilizzare l'impostazione predefinita, si usa

func ( "mia stringa", false);

No. In generale, Java non ha molto (qualsiasi) zucchero sintattico, in quanto hanno cercato di fare un linguaggio semplice.

No.

È possibile ottenere lo stesso comportamento passando un oggetto che ha impostazioni predefinite intelligenti. Ma ancora una volta dipende da cosa il vostro caso è a portata di mano.

Come è stato menzionato Scala, Kotlin è anche degno di nota. In Kotlin parametri di funzione possono avere valori predefiniti come bene e possono anche riferirsi ad altri parametri:

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

Come Scala, Kotlin corre sulla JVM e può essere facilmente integrato in progetti Java esistente.

Non è supportato, ma ci sono diverse opzioni come l'utilizzo del modello di parametri oggetto con lo zucchero sintassi:

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 questo esempio si costruisce ParameterObject con valori di default e sovrascriverli in istanza di classe sezione di inizializzazione { param1 = 10; param2 = "bar";}

Prova questa soluzione:

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

    return score;
}

È possibile utilizzare Java Method Invocation Builder per generare automaticamente il costruttore di default con valori.

Basta aggiungere @GenerateMethodInvocationBuilder alla classe, o interfaccia, e il @Default ai parametri nei metodi in cui si desidera valori predefiniti. Un costruttore verrà generato al momento della compilazione, utilizzando i valori di default specificati con le vostre annotazioni.

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

E allora si può richiamare i metodi.

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

O impostare uno dei valori predefiniti a qualcos'altro.

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

Un approccio simile a https://stackoverflow.com/a/13864910/2323964 che funziona in Java 8 è di utilizzare un'interfaccia con getter di default. Questo sarà più spazio bianco prolisso, ma è mockable, ed è ottimo per quando si dispone di un gruppo di casi in cui si vuole realmente attirare l'attenzione sui parametri.

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

Ora ho speso un bel po 'di tempo per capire come utilizzare questo con i metodi che restituiscono valori, e non ho visto alcun esempio finora, ho pensato che potrebbe essere utile aggiungere questo qui:

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

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

Ci sono una mezza dozzina o meglio questioni come questa, alla fine si arriva al modello di fabbrica statica ... vedere l'api crypto per questo. Ordina difficile da spiegare, ma credo che in questo modo: se si dispone di un costruttore, di default o in altro modo, l'unico modo per propagare lo stato al di là delle parentesi graffe è o per avere un valore booleano isValid; (Insieme con il NULL come valore di default v fallito costruttore) o un'eccezione che non è mai informativo quando ottenere di nuovo da parte degli utenti sul campo.

codice corretto sia dannato, scrivo mille costruttori di linea e di fare quello che mi serve. Trovo usando isValid a costruzione di un oggetto - in altre parole, due costruttori di linea - ma per qualche motivo sto migrazione verso il modello di fabbrica statica. Mi sembra proprio che si può fare molto se si in una chiamata di metodo, ci sono ancora sync () problemi, ma di default può essere 'sostituito' meglio (più sicuro)

Penso che quello che dobbiamo fare qui è affrontare la questione di null come valore di default vis-a-vis qualcosa String uno = new String ( ""); come variabile membro, quindi fare un controllo per null prima assegnazione stringa passata al costruttore.

Molto notevole la quantità di grezzo, informatica stratosferico fatto in Java.

C ++ e così via ha librerie vendor, sì. Java li può correre più veloce su server su larga scala grazie alla sua massiccia cassetta degli attrezzi. Studiare blocchi inizializzatore statico, stare con noi.

Ecco come ho fatto ... non è così conveniente, forse ad avere un 'argomento opzionale' contro il vostro parametro definito, ma ottiene il lavoro fatto:

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

Avviso posso richiamare lo stesso nome del metodo sia con solo una stringa o posso richiamare con una stringa e un valore booleano. In questo caso, l'impostazione WipeClean true sostituirà tutto il testo nel mio TextArea con la stringa fornita. Impostazione WipeClean false o lasciando fuori tutti insieme aggiunge semplicemente il testo fornito al TextArea.

Si noti anche io non sto ripetendo codice nei due metodi, sto solo aggiungendo la funzionalità di poter ripristinare il TextArea con la creazione di un nuovo metodo con lo stesso nome solo con il booleano aggiunto.

Io in realtà penso che questo sia un po 'più pulita di se Java fornito un 'argomento opzionale' per i nostri parametri in quanto ci sarebbe bisogno di poi il codice per i valori predefiniti ecc In questo esempio, non ho bisogno di preoccuparsi di nulla di tutto ciò . Sì, ho aggiunto ancora un altro metodo per la mia classe, ma è più facile da leggere nel lungo periodo, a mio modesto parere.

No, ma abbiamo alternativa sotto forma di funzione di sovraccarico.

chiamato quando nessun parametro passato

void operation(){

int a = 0;
int b = 0;

} 

chiamato quando è stata approvata "a" parametro

void operation(int a){

int b = 0;
//code

} 

chiamato quando il parametro b passò

void operation(int a , int b){
//code
} 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top