Pregunta ¿Admite Java los valores de parámetro predeterminados?


Encontré un código Java que tenía la siguiente estructura:

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

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

Sé que en C ++ puedo asignar un parámetro a un valor predeterminado. Por ejemplo:

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

¿Admite Java este tipo de sintaxis? ¿Hay alguna razón por la cual esta sintaxis de dos pasos es preferible?


1311
2018-06-15 18:04


origen


Respuestas:


No, la estructura que encontraste es cómo maneja Java (es decir, con sobrecarga en lugar de parámetros predeterminados).

Para constructores, Ver Java efectivo: Guía de lenguaje de programación Consejo del artículo 1 (Considere los métodos de fábrica estáticos en lugar de los constructores) si la sobrecarga se está complicando. Para otros métodos, puede cambiar el nombre de algunos casos o usar un objeto de parámetro. Esto es cuando tienes suficiente complejidad que la diferenciación es difícil. Un caso definido es cuando debe diferenciar usando el orden de los parámetros, no solo el número y tipo.


757
2018-06-15 18:14



No, pero puedes usar el Patrón de constructor, como se describe en esta respuesta de desbordamiento de pila.

Tal como se describe en la respuesta vinculada, el patrón del generador le permite escribir códigos como

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

en el que algunos campos pueden tener valores predeterminados o de lo contrario ser opcionales.


541
2018-06-15 19:20



Hay varias formas de simular parámetros predeterminados en Java:

  1. Método de sobrecarga.

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

    Una de las limitaciones de este enfoque es que no funciona si tiene dos parámetros opcionales del mismo tipo y cualquiera de ellos se puede omitir.

  2. Varargs.

    a) Todos los parámetros opcionales son del mismo 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) Los tipos de parámetros opcionales pueden ser diferentes:

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

    El principal inconveniente de este enfoque es que si los parámetros opcionales son de tipos diferentes, se pierde la comprobación del tipo estático. Además, si cada parámetro tiene un significado diferente, necesita alguna forma de distinguirlos.

  3. Nulos. Para abordar las limitaciones de los enfoques anteriores, puede permitir valores nulos y luego analizar cada parámetro en un cuerpo de método:

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

    Ahora se deben proporcionar todos los valores de los argumentos, pero los predeterminados pueden ser nulos.

  4. Clase opcional Este enfoque es similar a los nulos, pero utiliza la clase opcional Java 8 para los parámetros que tienen un valor predeterminado:

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

    Opcional hace que un contrato de método sea explícito para una persona que llama, sin embargo, uno puede encontrar esa firma demasiado detallada.

  5. Patrón de constructor El patrón de constructor se usa para constructores y se implementa mediante la introducción de una clase de generador por separado:

     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. Mapas. Cuando el número de parámetros es demasiado grande y para la mayoría de ellos se usan generalmente los valores predeterminados, puede pasar los argumentos del método como un mapa de sus nombres / valores:

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

Tenga en cuenta que puede combinar cualquiera de estos enfoques para lograr un resultado deseable.


348
2017-11-01 02:03



Tristemente no.


197
2018-06-15 18:05



Por desgracia sí.

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

podría escribirse en Java 1.5 como:

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

Pero si debes depender de cómo te sientes acerca del compilador generando un

new Boolean[]{}

para cada llamada.

Para múltiples parámetros eliminables:

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

podría escribirse en Java 1.5 como:

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

Esto coincide con la sintaxis de C ++, que solo permite los parámetros predeterminados al final de la lista de parámetros.

Más allá de la sintaxis, existe una diferencia cuando esto ha ejecutado el tipo de verificación de tiempo para los parámetros pasibles de errores y el tipo de C ++ los verifica durante la compilación.


76
2018-05-26 21:51



No, pero puedes emularlos fácilmente. Lo que en C ++ fue:

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

En Java, será una función sobrecargada:

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

Anteriormente se mencionó que los parámetros predeterminados causaban casos ambiguos en la sobrecarga de funciones. Eso simplemente no es cierto, podemos ver en el caso de C ++: sí, tal vez puede crear casos ambiguos, pero estos problemas se pueden manejar fácilmente. Simplemente no se desarrolló en Java, probablemente porque los creadores querían un lenguaje mucho más simple que C ++, si tenían razón, es otra cuestión. Pero la mayoría de nosotros no cree que use Java por su simplicidad.


35
2017-11-04 09:12



Puede hacer esto en Scala, que se ejecuta en JVM y es compatible con los programas de Java. http://www.scala-lang.org/

es decir

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

19
2017-07-21 16:26



Podría estar diciendo lo obvio aquí, pero ¿por qué no simplemente implementar el parámetro "predeterminado" usted mismo?

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

por defecto, usarías el éter

func ("mi cadena");

y si no desea utilizar el valor predeterminado, usaría

func ("mi cadena", falso);


11
2018-02-18 13:59



No. En general, Java no tiene mucho (ningún) azúcar sintáctico, ya que intentaron hacer un lenguaje simple.


6
2018-06-15 18:49



No.

Puede lograr el mismo comportamiento pasando un Objeto que tiene valores predeterminados inteligentes. Pero de nuevo depende de lo que su caso tiene a mano.


6
2018-06-15 18:16