Поддерживает ли Java значения параметров по умолчанию? - PullRequest
1489 голосов
/ 15 июня 2009

Я наткнулся на некоторый код Java, который имел следующую структуру:

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

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

Я знаю, что в C ++ я могу присвоить параметру значение по умолчанию. Например:

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

Поддерживает ли Java этот вид синтаксиса? Есть ли причины, по которым этот двухэтапный синтаксис предпочтительнее?

Ответы [ 20 ]

853 голосов
/ 15 июня 2009

Нет, структура, которую вы нашли, - это то, как Java обрабатывает ее (то есть с перегрузкой вместо параметров по умолчанию).

Для конструкторов, См. Эффективное Java: Руководство по языку программирования Подсказка к пункту 1 (Рассмотрим статические фабричные методы вместо конструкторов), если перегрузка становится сложной. Для других методов может помочь переименование некоторых случаев или использование объекта параметра. Это когда у вас достаточно сложности, чтобы дифференцировать трудно. В определенном случае вы должны различать, используя порядок параметров, а не только число и тип.

589 голосов
/ 15 июня 2009

Нет, но вы можете использовать шаблон Builder , как описано в этого ответа переполнения стека .

Как описано в связанном ответе, шаблон Builder позволяет писать код, подобный

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

, в котором некоторые поля могут иметь значения по умолчанию или иным образом быть необязательными.

416 голосов
/ 01 ноября 2013

Существует несколько способов имитации параметров по умолчанию в Java:

  1. Метод перегрузки.

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

    Одним из ограничений этого подхода является то, что он не работает, если у вас есть два необязательных параметра одного типа, и любой из них может быть опущен.

  2. с переменным числом аргументов.

    a) Все необязательные параметры относятся к одному типу:

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

    б) Типы необязательных параметров могут быть разными:

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

    Основным недостатком этого подхода является то, что если дополнительные параметры имеют разные типы, вы теряете статическую проверку типов. Кроме того, если каждый параметр имеет разное значение, вам нужен какой-то способ их различения.

  3. Нули. Чтобы устранить ограничения предыдущих подходов, вы можете разрешить нулевые значения и затем проанализировать каждый параметр в теле метода:

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

    Теперь должны быть указаны все значения аргументов, но значения по умолчанию могут быть нулевыми.

  4. Необязательный класс. Этот подход аналогичен пустым значениям, но использует Java 8 Необязательный класс для параметров со значением по умолчанию:

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

    Необязательно делает контракт метода явным для вызывающей стороны, однако такую ​​подпись можно найти слишком многословной.

  5. Шаблон Builder. Шаблон Builder используется для конструкторов и реализуется путем введения отдельного класса Builder:

     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. Карты. Если число параметров слишком велико и для большинства из них обычно используются значения по умолчанию, вы можете передать аргументы метода в виде карты их имен / значений:

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

Обратите внимание, что вы можете комбинировать любой из этих подходов для достижения желаемого результата.

222 голосов
/ 15 июня 2009

К сожалению, нет.

80 голосов
/ 27 мая 2010

К сожалению, да.

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

может быть записано в Java 1.5 как:

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

Но стоит ли вам зависеть от того, как вы относитесь к компилятору, генерирующему

new Boolean[]{}

за каждый звонок.

Для нескольких параметров по умолчанию:

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

может быть написано в Java 1.5 как:

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

Это соответствует синтаксису C ++, который допускает параметры по умолчанию только в конце списка параметров.

Помимо синтаксиса, есть разница, когда в нем выполняется проверка типов во время выполнения для переданных параметров по умолчанию и проверка типов C ++ во время компиляции.

36 голосов
/ 04 ноября 2014

Нет, но вы можете очень легко подражать им. Что в C ++ было:

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

В Java это будет перегруженная функция:

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

Ранее упоминалось, что параметры по умолчанию вызывают неоднозначные случаи перегрузки функций. Это просто не соответствует действительности, мы можем видеть в случае с C ++: да, возможно, он может создавать неоднозначные случаи, но эти проблемы могут быть легко решены. Он просто не был разработан в Java, возможно потому, что создатели хотели гораздо более простой язык, чем был C ++ - если они имели право, это другой вопрос. Но большинство из нас не думают, что он использует Java из-за его простоты.

20 голосов
/ 21 июля 2011

Вы можете сделать это в Scala, который работает на JVM и совместим с программами Java. http://www.scala -lang.org /

т.е.

class Foo(var prime: Boolean = false, val rib: String)  {}
11 голосов
/ 18 февраля 2013

Я мог бы заявить здесь об очевидном, но почему бы просто не реализовать параметр «по умолчанию» самостоятельно?

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

по умолчанию вы бы использовали эфир

func ("моя строка");

и если вы не хотите использовать значение по умолчанию, вы должны использовать

func ("моя строка", ложь);

10 голосов
/ 22 ноября 2017

Нет , но самый простой способ реализовать это :

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

Или вместо троичного оператора вы можете использовать, если:

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}
6 голосов
/ 26 ноября 2017

Как упоминалось в Scala, Kotlin также стоит упомянуть. В параметрах функции Kotlin также могут быть значения по умолчанию, и они могут даже ссылаться на другие параметры:

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

Как и Scala, Kotlin работает на JVM и может быть легко интегрирован в существующие проекты Java.

...