Необов’язкові параметри Java


812

Як я можу використовувати додаткові параметри на Java? Яка специфікація підтримує необов'язкові параметри?

Відповіді:


516

varargs могли це зробити (певним чином). Крім цього, всі змінні в декларації методу повинні бути надані. Якщо ви хочете, щоб змінна була необов'язковою, ви можете перевантажити метод, використовуючи підпис, який не потребує параметра.

private boolean defaultOptionalFlagValue = true;

public void doSomething(boolean optionalFlag) {
    ...
}

public void doSomething() {
    doSomething(defaultOptionalFlagValue);
}

На мою думку, перевантаження методом - це хороша відповідь, тоді як варагги - дуже погана відповідь. Будь ласка, видаліть коментар до варагрів або поясніть серйозний недолік, спричинений можливістю більше одного поверненого значення.
Андреас

Додавання глобальних змінних може спричинити деякі інші проблеми.
Олена Куй

1652

Існує кілька способів моделювання необов'язкових параметрів на 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());
    

    Необов'язково робить контракт методу явним для абонента, однак, такий підпис може бути занадто багатослівним.

    Оновлення: Java 8 включає клас java.util.Optionalпоза вікном, тому немає необхідності використовувати guava саме з цієї причини в Java 8. Хоча назва методу дещо інша.

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

    У Java 9 такий підхід став простішим:

        @SuppressWarnings("unchecked")
        static <T> T getParm(Map<String, Object> map, String key, T defaultValue)
        {
            return (map.containsKey(key)) ? (T) map.get(key) : defaultValue;
        }
    
        void foo(Map<String, Object> parameters) {
            String a = getParm(parameters, "a", "");
            int b = getParm(parameters, "b", 0);
            // d = ...
        }
    
        foo(Map.of("a","a",  "b",2,  "d","value"));
    

Зверніть увагу, що ви можете комбінувати будь-який із цих підходів для досягнення бажаного результату.


2
@ Віталій Федоренко Хм, я думаю, ви зробили невелику помилку копіювання-вставки в №6: Ви перевіряєте тип Integer, хоча ваша aзмінна - String (літ є правильним).
Невідомий Id

5
@Aetos ваше посилання мертве.
Робіно

2
Альтернативне посилання @Robino, стверджуючи, що необов'язково не слід використовувати тут як параметр . Незважаючи на використання Optionalпараметра в якості одного з варіантів, ця відповідь є дуже хорошою.
Дерік

Найбільш розповсюджене рішення вищезгаданої проблеми - це, мабуть, метод перевантаження. Хоча я ніколи не був фанатом цього. Особисто я вважаю за краще, якщо вони додають якийсь ідентифікатор для параметрів конфігурації.
Олександр Гейм

1
це, мабуть, найкраща відповідь - охоплює всіх
xproph

104

Є додаткові параметри з Java 5.0. Просто оголосьте свою функцію так:

public void doSomething(boolean... optionalFlag) {
    //default to "false"
    //boolean flag = (optionalFlag.length >= 1) ? optionalFlag[0] : false;
}

ви могли б зателефонувати з doSomething();або doSomething(true);зараз.


13
Це насправді правильна відповідь. Це просто і компактно. Просто пам’ятайте, що ви могли отримати більше одного параметра, тому Java поміщає їх у масив. Наприклад, щоб отримати один параметр, ви спершу перевірите вміст масиву: 'code' boolean flag = (optionalFlag.length <1)? False: optionalFlag [0];
Сальвадор Валенсія

46
Ні, це не правильна відповідь, оскільки це дозволяє не один необов'язковий параметр, а будь-яку кількість необов'язкових параметрів. Хоча це близько до того, що хоче ОП, це не те саме. Це потенційне джерело помилок і непорозумінь, щоб прийняти більше параметрів, ніж потрібно.
sleske

2
Цей спосіб працює, якщо у вас є лише один необов'язковий параметр, але вам потрібно перевірити довжину масиву тощо, щоб він не був надто чистим: | Якщо ви хочете необов'язковий параметр "один параметр", ви можете також оголосити два способи (один перевантажений doSomething() { doSomething(true); }без масивів для вирішення, двозначності
rogerdpack

Якщо є кілька необов'язкових параметрів, це працює лише у тому випадку, якщо вони є одним типом даних. Добре, ви можете зробити тип Object, але це стає справді некрасиво.
Джей

Як зазначав @sleske, використання varargs має жахливий недолік, що дозволяє передавати більше одного значення. OP чітко запитав про рішення для "необов'язкового", що означає або одне значення, або нуль. Яким чином це може вважати гарною відповіддю деяких людей.
Андреас

99

Ви можете використовувати щось подібне:

public void addError(String path, String key, Object... params) { 
}

paramsЗмінна є необов'язковим. Це трактується як нульовий масив Об'єктів.

Як не дивно, я нічого не міг знайти про це в документації, але це працює!

Це "нове" в Java 1.5 і вище (не підтримується в Java 1.4 або новіших версіях).

Я бачу, що користувач bhoot згадав про це теж нижче.


55

На жаль, Java не підтримує параметри за замовчуванням безпосередньо.

Однак я написав набір анотацій JavaBean, і одна з них підтримує параметри за замовчуванням, такі як:

protected void process(
        Processor processor,
        String item,
        @Default("Processor.Size.LARGE") Size size,
        @Default("red") String color,
        @Default("1") int quantity) {
    processor.process(item, size, color, quantity);
}
public void report(@Default("Hello") String message) {
    System.out.println("Message: " + message);
}

Процесор анотацій генерує метод перевантаження, щоб правильно підтримувати це.

Див. Http://code.google.com/p/javadude/wiki/Анотації

Повний приклад на веб- сторінці http://code.google.com/p/javadude/wiki/AnnotationsDefaultParametersExample


53

Немає додаткових параметрів у Java. Що ви можете зробити, це перевантажити функції та передати значення за замовчуванням.

void SomeMethod(int age, String name) {
    //
}

// Overload
void SomeMethod(int age) {
    SomeMethod(age, "John Doe");
}

23

Згадано про VarArgs та перевантаження. Ще один варіант - це модель Builder, яка виглядатиме приблизно так:

 MyObject my = new MyObjectBuilder().setParam1(value)
                                 .setParam3(otherValue)
                                 .setParam6(thirdValue)
                                 .build();

Хоча ця закономірність була б найбільш підходящою для того, коли потрібні необов’язкові параметри в конструкторі.


Я хочу додати залежність параметрів для сказати that.let, я хочу встановити param3, тільки якщо я встановив param1. Наприклад, наприклад. Я хочу встановити повідомлення про хід, лише якщо я встановив прогрес видимим. isProgressVisible (). setProgressMessage ("завантаження"). як я можу цього досягти?
Харшал Бхатт

14

У JDK> 1.5 ви можете використовувати його так;

public class NewClass1 {

    public static void main(String[] args) {

        try {
            someMethod(18); // Age : 18
            someMethod(18, "John Doe"); // Age & Name : 18 & John Doe
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void someMethod(int age, String... names) {

        if (names.length > 0) {
            if (names[0] != null) {
                System.out.println("Age & Name : " + age + " & " + names[0]);
            }
        } else {
            System.out.println("Age : " + age);
        }
    }
}

7

Ви можете зробити щось, використовуючи такий метод перевантаження.

 public void load(String name){ }

 public void load(String name,int age){}

Також ви можете використовувати @Nullable анотацію

public void load(@Nullable String name,int age){}

просто передайте null як перший параметр.

Якщо ви передаєте змінну одного типу, ви можете скористатися цією

public void load(String name...){}

7

Коротка версія :

Використовуючи три крапки :

public void foo(Object... x) {
    String first    =  x.length > 0 ? (String)x[0]  : "Hello";
    int duration    =  x.length > 1 ? Integer.parseInt((String) x[1])     : 888;
}   
foo("Hii", ); 
foo("Hii", 146); 

(на основі відповіді @ VitaliiFedorenko)


2

Перевантаження добре, але якщо є багато змінних, яким потрібно значення за замовчуванням, ви закінчите:

public void methodA(A arg1) {  }
public void methodA( B arg2,) {  }
public void methodA(C arg3) {  }
public void methodA(A arg1, B arg2) {  }
public void methodA(A arg1, C arg3) {  }
public void methodA( B arg2, C arg3) {  }
public void methodA(A arg1, B arg2, C arg3) {  }

Тому я б запропонував використовувати Аргумент змінної, що надається Java. Ось посилання на пояснення.


використання варагрів вважається поганою практикою. якщо системі потрібні такі (згадані вище) методи, то слід подумати про новий дизайн, оскільки дизайн класу виглядає погано.
Діабло

2
Ще гірша практика виправдовувати недоліки на Java і звинувачувати інших у поганій практиці за те, щоб помітити ці незручності та придумувати обхідні шляхи.
BrianO

1
Будь ласка, додайте всю відповідну інформацію до своєї відповіді, а не
Ніко Хааз

2

Ви можете використовувати клас, який працює як конструктор, щоб містити такі необов'язкові значення, як це.

public class Options {
    private String someString = "default value";
    private int someInt= 0;
    public Options setSomeString(String someString) {
        this.someString = someString;
        return this;
    }
    public Options setSomeInt(int someInt) {
        this.someInt = someInt;
        return this;
    }
}

public static void foo(Consumer<Options> consumer) {
    Options options = new Options();
    consumer.accept(options);
    System.out.println("someString = " + options.someString + ", someInt = " + options.someInt);
}

Використовуйте як

foo(o -> o.setSomeString("something").setSomeInt(5));

Вихід є

someString = something, someInt = 5

Щоб пропустити всі необов'язкові значення, які вам доведеться назвати так, foo(o -> {});або якщо ви хочете, ви можете створити другий foo()метод, який не приймає необов'язкові параметри.

Використовуючи такий підхід, ви можете задавати необов'язкові значення в будь-якому порядку без будь-якої двозначності. Ви також можете мати параметри різних класів на відміну від varargs. Цей підхід буде ще кращим, якщо ви можете використовувати анотації та генерацію коду для створення класу «Параметри».


1

Java тепер підтримує додаткові додатки в 1.8, я застряг у програмуванні на android, тому я використовую нулі, поки не зможу перефактурувати код для використання додаткових типів.

Object canBeNull() {
    if (blah) {
        return new Object();
    } else {
        return null;
    }
}

Object optionalObject = canBeNull();
if (optionalObject != null) {
    // new object returned
} else {
    // no new object returned
}

чи можете ви надати приклад, будь ласка?
sepehr

1

Аргументи за замовчуванням не можна використовувати в Java. У C #, C ++ та Python ми можемо їх використовувати ..

У Java нам потрібно використовувати 2 способи (функції) замість одного із параметрами за замовчуванням.

Приклад:

Stash(int size); 

Stash(int size, int initQuantity);

http://parvindersingh.webs.com/apps/forums/topics/show/8856498-java-how-to-set-default-parameters-values-like-c-


28
Новіші версії C # дозволяють параметри за замовчуванням.
Соггер

2
java також має параметр "..." для будь-якого параметра.
Cacho Santa

0

Це старе питання, можливо, ще до того, як було введено фактичний необов'язковий тип, але в наші дні ви можете врахувати декілька речей: - використовувати перевантаження методу - використовувати опціональний тип, який має перевагу уникнути передачі NULL навколо опціонального типу, введеного в Java 8 перед тим, як його зазвичай використовували від сторонніх власників, таких як гуава Google. Використання необов'язкових як параметрів / аргументів можна вважати надмірним використанням, оскільки основною метою було використання його як часу повернення.

Посилання: https://itcodehub.blogspot.com/2019/06/using-optional-type-in-java.html


0

Ми можемо зробити необов'язковий параметр методом перевантаження або використанням DataType ...

| * | Спосіб перевантаження:

RetDataType NameFnc(int NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(String NamePsgVar)
{
    // |* Code Todo *|
    return RetVar;
}

RetDataType NameFnc(int NamePsgVar1, String NamePsgVar2)
{
    // |* Code Todo *|
    return RetVar;
}

Найпростіший спосіб

| * | Тип даних ... може бути необов'язковим параметром

RetDataType NameFnc(int NamePsgVar, String... stringOpnPsgVar)
{
    if(stringOpnPsgVar.length == 0)  stringOpnPsgVar = DefaultValue; 

    // |* Code Todo *|
    return RetVar;
}


8
RetDtaTyp... серйозно? Є RetDataTypeзанадто важко надрукувати?
Олександр - Відновити Моніку

Чи можете ви додати ще якесь пояснення до цього коду? Що роблять усі ці дивні змінні?
Ніко Хааз

Змінили назви змінних для зручності розуміння ...
Sujay UN

0

Якщо ви плануєте використовувати інтерфейс з декількома параметрами , можна скористатися наступною структурною схемою та застосувати або застосувати заміщення - метод, заснований на ваших вимогах .

public abstract class Invoker<T> {
    public T apply() {
        return apply(null);
    }
    public abstract T apply(Object... params);
}

0

Якщо це кінцева точка API, елегантним способом є використання анотацій "Весна":

@GetMapping("/api/foos")
@ResponseBody
public String getFoos(@RequestParam(required = false, defaultValue = "hello") String id) { 
    return innerFunc(id);
}

Зауважте в цьому випадку, що InternalFunc вимагатиме змінної, і оскільки це не кінцева точка api, не можна використовувати цю весняну примітку, щоб зробити її необов'язковою. Довідка: https://www.baeldung.com/spring-request-param

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.