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


я наткнулся на некоторый код 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 такой синтаксис? Есть ли причины, почему этот двухэтапный синтаксис предпочтительнее?

19 1340

19 ответов:

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

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

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

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

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

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

существует несколько способов моделирования параметров по умолчанию в 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. С varargs.

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

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

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

  4. дополнительный класс. этот подход похож на null, но использует 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());
    

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

  5. шаблон строителя. шаблон строителя используется для конструкторы и реализуется путем введения отдельного класса 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")); 
    

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

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

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

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++ проверяет их во время компиляции.

нет, но вы можете очень легко подражать им. Что в 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 из-за его простоты.

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

т. е.

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

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

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

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

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

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

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

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

нет.

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

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

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

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

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

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

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

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

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

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

    return score;
}

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

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

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

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

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

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

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, создавая новый метод с тем же именем только с добавленным логическим значением.

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

аналогичный подход к https://stackoverflow.com/a/13864910/2323964 что работает в 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; }
        });
    }
}

вы можете использовать Java Method Invocation 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);

или set любое из значений по умолчанию для чего-то другого.

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

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

вызывается, когда параметр не передан

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
}