Поддерживает ли 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 ответов


нет, найденная структура-это то, как 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);
    

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

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

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

     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[]{}

для каждого вызова.

для нескольких параметров defaultable:

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++, который разрешает только параметры по умолчанию в конце списка параметров.

помимо синтаксиса, есть разница, когда это проверка типа времени выполнения для переданных параметров defaultable и тип 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)  {}

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


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


нет.

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


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

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

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

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

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

public myParameterizedFunction(String param1, int param2) {

    this(param1, param2, false);
}

Как упоминалось в Scala,Котлин также стоит упомянуть. В 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;
}

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

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

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

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

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


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

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.

также обратите внимание, что я не повторяю код в двух методах, я просто добавляю функциональность возможности сброса 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 Метод Вызова 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
}