Поддерживает ли Java значения параметров по умолчанию?

1087

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

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

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

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

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

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

  • 73
    Нет. Однако шаблон Builder может помочь.
  • 32
    Я очень скучаю по этой функции. Это очень помогает при изменении существующего кода, чтобы получить дополнительный параметр для функции или конструктора
Показать ещё 5 комментариев
Теги:
methods
parameters
method-overloading
default-value

19 ответов

688
Лучший ответ
  • 96
    @JarrodRoberson: Статические фабричные методы не более вредны, чем new . Они используются все время в новом коде. Построители простых объектов значений часто являются результатом чрезмерного проектирования.
Показать ещё 4 комментария
468

Нет, но вы можете использовать Builder Pattern, как описано в этот ответ.

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

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

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

  • 111
    Наконец, отличный пример шаблона «Строитель» размером менее 2 страниц.
  • 13
    Мне любопытно, зачем нам нужен класс построителя при использовании шаблона построителя. Я думал о Студенте s1 = new Student (). Name ("Spicolo"). Age (16) .motto ("Алоха, мистер Хэнд);
Показать ещё 6 комментариев
307

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

  • Перегрузка метода.

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

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

  • переменной длины.

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

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

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

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

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

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

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

  • Необязательный класс. Этот подход похож на нуль, но использует 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());
    

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

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

    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");
        }
        if (parameters.containsKey("b")) { 
            //... 
        }
        //...
    }
    
    foo(ImmutableMap.<String, Object>of(
        "a", "a",
        "b", 2, 
        "d", "value")); 
    

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

  • 1
    Хорошее объяснение. Я никогда не видел возвращаемые значения, используемые так. Для 5), что делает return this сделать? Кроме того, не FooBuilder().setA("a").build(); поскольку (по определению) конструктор вызывается первым, а FooBuilder() возвращает значение, разве это не означает .setA("a"): разве нет возможности быть вызванным?
  • 3
    @Celeritas return this возвращает тот же объект , на котором был вызван метод (в примере, FooBuilder ). Это позволяет объединять методы в одном операторе, воздействующем на один и тот же объект: new FooBuilder().setA(..).setB(..).setC(..) т. Д., В отличие от вызова каждого метода в отдельном операторе.
Показать ещё 6 комментариев
159

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

  • 31
    Это так грустно? Это может привести к потенциально неоднозначным сигнатурам функций.
  • 1
    Я согласен с Треем. Это не грустно :) и так как параметры по умолчанию существуют долгое время, я думаю, у инженеров Java есть веские причины не включать его;)
Показать ещё 11 комментариев
71

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

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

Это соответствует синтаксису С++, который разрешает только дефолтные параметры в конце списка параметров.

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

  • 14
    Умно, но varargs (...) можно использовать только для конечного параметра, который более ограничен, чем те языки, которые поддерживают параметры по умолчанию.
  • 2
    Я добавил пример для нескольких параметров по умолчанию.
Показать ещё 7 комментариев
25

Нет, но вы можете легко имитировать их. Что в С++ было:

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

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

  • 6
    Основная идея нотации значений по умолчанию в C # состоит именно в том, чтобы избежать этого стандартного кодирования и иметь только один конструктор вместо многих.
  • 1
    @KolyaIvankov Я не знаю C #, но я знаю C ++, где рассуждения такие же. Я не знаю, что лучше, но я думаю, что на самом деле тот же самый шаблонный код генерируется компилятором в случае C ++ / C #, и он входит в окончательный двоичный файл.
Показать ещё 4 комментария
19

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

то есть.

class Foo(var prime: Boolean = false, val rib: String)  {}
  • 53
    Привести совершенно новый язык, чтобы получить одну не очень общую черту?
  • 8
    @ om-nom-nom: Java никогда не должно существовать. Заявление о том, что функция не используется, равнозначно тому, что никому не нужно, это говорит о том, что Java не была популярна до ее изобретения, означает, что Гослинг не должен начинать ее разработку.
Показать ещё 2 комментария
10

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

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

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

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

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

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

  • 10
    Постер спросил, можно ли избежать этого (довольно уродливого) паттерна ... ;-) В более современных языках (таких как c #, Scala) вам не нужны эти дополнительные перегрузки, которые только создают больше строк кода. До некоторой степени вы можете использовать varargs тем временем (static int max (int ... array) {}), но они являются лишь очень уродливым решением.
  • 2
    Перегрузка не безобразна и имеет много преимуществ, например, различные вызовы методов с разными сигнатурами могут выполнять разные функции. //This is better public class Foo() { /* This does something */ public void func(String s){ //do something } /* This does something else with b */ public void func(String s, boolean b){ // b was passed } } //Than this public class Foo() { /* This does something unless b = value, then it does something else */ public void func(String s, boolean b = value){ If (b){ // Do Something } else{ // Do something else } } }
Показать ещё 2 комментария
5

Нет. В общем, у Java нет большого (любого) синтаксического сахара, поскольку они пытались сделать простой язык.

  • 25
    Не совсем. Горькая правда в том, что у команды был плотный график, и у нее не было времени на синтаксический сахар. Зачем еще const и goto быть зарезервированными ключевыми словами, которые не имеют реализации? - Особенно const это то, что я с горечью скучаю, - final не замена, и они это знали. - И если вы приняли осознанное решение никогда не применять goto вам не нужно резервировать ключевое слово. - А потом в Java Team обманули, сделав break основе Label, и continue так же мощно, как goto на Паскаль.
  • 0
    «Простой, объектно-ориентированный и знакомый» действительно был целью разработки - см. Oracle.com/technetwork/java/intro-141325.html
Показать ещё 1 комментарий
4

Нет.

Вы можете добиться такого же поведения, передав объект с интеллектуальными значениями по умолчанию. Но опять-таки это зависит от вашего дела.

3

Попробуйте это решение:

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

    return score;
}
3

Он не поддерживается, но есть несколько параметров, таких как использование шаблона объекта параметров с некоторым синтаксическим сахаром:

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

В этом примере мы построим ParameterObject со значениями по умолчанию и переопределим их в разделе инициализации экземпляра класса { param1 = 10; param2 = "bar";}

1

NO, но у нас есть альтернатива в виде перегрузки функций.

когда не передан параметр

void operation(){

int a = 0;
int b = 0;

} 

вызываемый при передаче "a"

void operation(int a){

int b = 0;
//code

} 

вызываемый при прохождении параметра b

void operation(int a , int b){
//code
} 
1

Аналогичный подход к https://stackoverflow.com/questions/997482/does-java-support-default-parameter-values, который работает в Java 8, заключается в использовании интерфейса с использованием гейтеров по умолчанию. Это будет более условно, но является макетным, и это здорово, когда у вас есть куча примеров, где вы действительно хотите привлечь внимание к параметрам.

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

Есть полдюжины или более важные проблемы, такие как это, в конечном итоге вы попадаете в статический шаблон factory... см. криптографию api для этого. Сортировка сложно объяснить, но подумайте об этом так: если у вас есть конструктор, по умолчанию или иначе, единственный способ распространения состояния за фигурными фигурными скобками - либо иметь логическое значение isValid; (вместе с нулевым значением по умолчанию v сбойным конструктором) или генерирует исключение, которое никогда не бывает информативным, когда оно возвращается от пользователей поля.

Код. Исправьте, я пишу тысячи конструкторов строк и делаю то, что мне нужно. Я нахожу использование isValid при построении объекта, другими словами, двухстрочных конструкторов, но по какой-то причине я переношусь на статический шаблон factory. Мне кажется, вы можете много сделать, если вы в вызове метода, есть еще проблемы с синхронизацией(), но значения по умолчанию могут быть "заменены" лучше (безопаснее)

Я думаю, что нам нужно сделать здесь ошибку null в качестве значения по умолчанию vis-a-vis что-то String one = new String (""); в качестве переменной-члена, а затем выполнить проверку на нуль перед назначением строки, переданной конструктору.

Очень примечательно количество сырой, стратосферной компьютерной науки, сделанной на Java.

С++ и т.д. есть поставщики libs, да. Java может обогнать их на больших серверах благодаря массивной панели инструментов. Изучите блоки статического инициализатора, оставайтесь с нами.

0

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

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

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

0

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

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

Вы можете использовать Java Invoice Builder для автоматического создания построителя со значениями по умолчанию.

Просто добавьте @GenerateMethodInvocationBuilder к классу или интерфейсу и @Default к параметрам в методах, где вы хотите значения по умолчанию. Во время компиляции будет создан строитель, используя значения по умолчанию, указанные вами в аннотациях.

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

И затем вы можете вызвать методы.

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

Или установите любое значение по умолчанию для чего-то еще.

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

Вот как я это сделал... это не так удобно, как наличие "необязательного аргумента" против вашего определенного параметра, но он выполняет свою работу:

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

Заметьте, что я могу вызывать одно и то же имя метода либо с помощью строки, либо я могу вызвать ее со строкой и логическим значением. В этом случае установка wipeClean в true заменит весь текст в моей TextArea на предоставленную строку. Установка wipeClean на false или оставление его вместе просто добавляет предоставленный текст в TextArea.

Также обратите внимание, что я не повторяю код в двух методах, я просто добавляю функциональность возможности reset TextArea, создавая новый метод с тем же именем только с добавленным булевым.

Я на самом деле думаю, что это немного чище, чем если бы Java предоставила "необязательный аргумент" для наших параметров, так как нам нужно было бы затем кодировать значения по умолчанию и т.д. В этом примере мне не нужно беспокоиться ни о какой из этих, Да, я добавил еще один метод для своего класса, но его легче читать в долгосрочной перспективе по моему скромному мнению.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню