Подтвердить что ты не робот

Именованный параметр идиом в Java

Как реализовать именованный Именованный Параметр в Java? (особенно для конструкторов)

Я ищу синтаксис Objective-C, а не тот, который используется в JavaBeans.

Небольшой пример кода будет в порядке.

Спасибо.

4b9b3361

Ответ 1

Лучшая Java-идиома, которая, как мне кажется, для моделирования аргументов ключевого слова в конструкторах - это шаблон Builder, описанный в Эффективный Java 2nd Edition.

Основная идея состоит в том, чтобы иметь класс Builder, который имеет сеттеры (но обычно не геттеры) для разных параметров конструктора. Также существует метод build(). Класс Builder часто является (статическим) вложенным классом класса, который он использовал для сборки. Конструктор внешнего класса часто является частным.

Конечный результат выглядит примерно так:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setSize(int size) {
      this.size = size;
      return this;
    }

    public Builder setColor(Color color) {
      this.color = color;
      return this;
    }

    public Builder setName(String name) {
      this.name = name;
      return this;
    }

    // you can set defaults for these here
    private int size;
    private Color color;
    private String name;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    size = builder.size;
    color = builder.color;
    name = builder.name;
  }

  private final int size;
  private final Color color;
  private final String name;

  // The rest of Foo goes here...
}

Чтобы создать экземпляр Foo, вы тогда напишите что-то вроде:

Foo foo = Foo.builder()
    .setColor(red)
    .setName("Fred")
    .setSize(42)
    .build();

Основные оговорки:

  • Настройка шаблона довольно многословна (как вы можете видеть). Вероятно, это не стоит, кроме классов, которые вы планируете создавать во многих местах.
  • Нет никакой проверки времени компиляции, когда все параметры были указаны ровно один раз. Вы можете добавить проверки выполнения во время выполнения, или использовать это только для необязательных параметров, а также для нормальных параметров требуемых параметров для Foo или конструктора Builder. (Люди обычно не беспокоятся о том, что один и тот же параметр задается несколько раз.)

Вы также можете проверить этот пост в блоге (не я).

Ответ 2

Это стоит упомянуть:

Foo foo = new Foo() {{
    color = red;
    name = "Fred";
    size = 42;
}};

так называемый инициализатор с двойной привязкой. Это фактически анонимный класс с инициализатором экземпляра.

Ответ 3

Вы также можете попробовать следовать совету: http://www.artima.com/weblogs/viewpost.jsp?thread=118828

int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);

Он многословный на сайте вызова, но в целом дает наименьшие издержки.

Ответ 4

Стиль Java 8:

public class Person {
    String name;
    int age;

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    static PersonWaitingForName create() {
        return name -> age -> new Person(name, age);
    }

    static interface PersonWaitingForName {
        PersonWaitingForAge name(String name);
    }

    static interface PersonWaitingForAge {
        Person age(int age);
    }

    public static void main(String[] args) {

        Person charlotte = Person.create()
            .name("Charlotte")
            .age(25);

    }
}
  • Именованные параметры
  • установить порядок аргументов
  • статическая проверка → без безымянного лица возможно
  • трудно переключать аргументы одного и того же типа случайно (как это возможно в конструкторах телескопа)

Ответ 5

Если вы используете Java 6, вы можете использовать переменные параметры и импортировать статические данные для получения гораздо лучшего результата. Подробная информация об этом содержится в:

http://zinzel.blogspot.com/2010/07/creating-methods-with-named-parameters.html

Короче говоря, у вас может быть что-то вроде:

go();
go(min(0));
go(min(0), max(100));
go(max(100), min(0));
go(prompt("Enter a value"), min(0), max(100));

Ответ 6

Здесь немного вариации техники, приведенной в Joshua Bloch Effective Java. Здесь я попытался сделать код клиента более читаемым (или, возможно, больше DSLish).

/**
 * Actual class for which we want to implement a 
 * named-parameter pseudo-constructor
 */
class Window{
    protected int x, y, width, height;
    protected boolean isResizable;
    protected String title;

    public void show(){
        // Show the window
        System.out.printf("Window \"%s\" set visible.%n",title);
    }

    /**
     * This class is only used to set the parameter values
     */
    static class HavingProperties extends Window{

        public HavingProperties x(int value){
            this.x=value;
            return this;
        }

        public HavingProperties y(int value){
            this.y=value;
            return this;
        }

        public HavingProperties width(int value){
            this.width=value;
            return this;
        }

        public HavingProperties height(int value){
            this.height=value;
            return this;
        }

        public HavingProperties resizable(boolean value){
            this.isResizable=value;
            return this;
        }

        public HavingProperties title(String value){
            this.title=value;
            return this;
        }
    }
}

public class NamedParameterIdiomInAction {
    public static void main(String... args){
        Window window=new Window.HavingProperties().x(10).y(10).width(100).
                height(100).resizable(true).title("My App");
        window.show();
    }
}

Обратите внимание, что с этим вариантом вы также можете дать значимые имена вашим псевдоконструкторам.

Ответ 7

Java не поддерживает Objective-C -именованные параметры для конструкторов или аргументов метода. Кроме того, на самом деле это не Java-способ делать что-то. В java типичный шаблон содержит многословные классы и члены. Классы и переменные должны быть существительными, а имя метода должно быть глаголом. Я предполагаю, что вы можете проявить творческий подход и отклониться от соглашений об именах Java и эмулировать парадигму Objective-C хакерским способом, но это не будет особенно полезно среднему разработчику Java, которому поручено поддерживать ваш код. При работе на любом языке вам следует придерживаться конвенций языка и сообщества, особенно при работе в команде.

Ответ 8

Что насчет

public class Tiger {
String myColor;
int    myLegs;

public Tiger color(String s)
{
    myColor = s;
    return this;
}

public Tiger legs(int i)
{
    myLegs = i;
    return this;
}
}

Tiger t = new Tiger().legs(4).color("striped");

Ответ 9

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

public class Something {

    String name;
    int size; 
    float weight;

    public Something(String name, int size, float weight) {
        this.name = name;
        this.size = size;
        this.weight = weight;
    }

    public static String name(String name) { 
        return name; 
    }

    public static int size(int size) {
        return size;
    }

    public float weight(float weight) {
        return weight;
    }

}

Использование:

import static Something.*;

Something s = new Something(name("pen"), size(20), weight(8.2));

Ограничения по сравнению с реальными именованными параметрами:

  • аргумент аргумента релевантен
  • списки переменных аргументов невозможны с помощью одного конструктора
  • вам нужен метод для каждого аргумента
  • не лучше, чем комментарий (new Something (/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2))

Если у вас есть выбор, посмотрите Scala 2.8. http://www.scala-lang.org/node/2075

Ответ 10

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

public class Person {
   String name;
   int age;

   // name property
   // getter
   public String name() { return name; }

   // setter
   public Person name(String val)  { 
    name = val;
    return this;
   }

   // age property
   // getter
   public int age() { return age; }

   // setter
   public Person age(int val) {
     age = val;
     return this;
   }

   public static void main(String[] args) {

      // Addresses named parameter

      Person jacobi = new Person().name("Jacobi").age(3);

      // Addresses property style

      System.out.println(jacobi.name());
      System.out.println(jacobi.age());

      //...

      jacobi.name("Lemuel Jacobi");
      jacobi.age(4);

      System.out.println(jacobi.name());
      System.out.println(jacobi.age());
   }

}

Ответ 11

Идиома, поддерживаемая karg library, заслуживает рассмотрения:

class Example {

    private static final Keyword<String> GREETING = Keyword.newKeyword();
    private static final Keyword<String> NAME = Keyword.newKeyword();

    public void greet(KeywordArgument...argArray) {
        KeywordArguments args = KeywordArguments.of(argArray);
        String greeting = GREETING.from(args, "Hello");
        String name = NAME.from(args, "World");
        System.out.println(String.format("%s, %s!", greeting, name));
    }

    public void sayHello() {
        greet();
    }

    public void sayGoodbye() {
        greet(GREETING.of("Goodbye");
    }

    public void campItUp() {
        greet(NAME.of("Sailor");
    }
}

Ответ 12

Это вариант шаблона Builder, описанный Лоуренсом выше.

Я очень много использую это (в подходящих местах).

Основное различие заключается в том, что в этом случае Builder является immuatable. Это имеет то преимущество, что его можно повторно использовать и поточно-безопасным.

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

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

С другой стороны, если вам нужно создавать объекты с изменяющимися параметрами, это немного успокаивает некоторые издержки. (но эй, вы можете комбинировать статическую/динамическую генерацию с пользовательскими методами build)

Вот пример кода:

public class Car {

    public enum Color { white, red, green, blue, black };

    private final String brand;
    private final String name;
    private final Color color;
    private final int speed;

    private Car( CarBuilder builder ){
        this.brand = builder.brand;
        this.color = builder.color;
        this.speed = builder.speed;
        this.name = builder.name;
    }

    public static CarBuilder with() {
        return DEFAULT;
    }

    private static final CarBuilder DEFAULT = new CarBuilder(
            null, null, Color.white, 130
    );

    public static class CarBuilder {

        final String brand;
        final String name;
        final Color color;
        final int speed;

        private CarBuilder( String brand, String name, Color color, int speed ) {
            this.brand = brand;
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        public CarBuilder brand( String newBrand ) {
            return new CarBuilder( newBrand, name, color, speed );
        }
        public CarBuilder name( String newName ) {
            return new CarBuilder( brand, newName, color, speed );
        }
        public CarBuilder color( Color newColor ) {
            return new CarBuilder( brand, name, newColor, speed );
        }
        public CarBuilder speed( int newSpeed ) {
            return new CarBuilder( brand, name, color, newSpeed );
        }
        public Car build() {
            return new Car( this );
        }
    }

    public static void main( String [] args ) {

        Car porsche = Car.with()
                .brand( "Porsche" )
                .name( "Carrera" )
                .color( Color.red )
                .speed( 270 )
                .build()
                ;

        // -- or with one default builder

        CarBuilder ASSEMBLY_LINE = Car.with()
                .brand( "Jeep" )
                .name( "Cherokee" )
                .color( Color.green )
                .speed( 180 )
                ;

        for( ;; ) ASSEMBLY_LINE.build();

        // -- or with custom default builder:

        CarBuilder MERCEDES = Car.with()
                .brand( "Mercedes" )
                .color( Color.black )
                ;

        Car c230 = MERCEDES.name( "C230" ).speed( 180 ).build(),
            clk = MERCEDES.name( "CLK" ).speed( 240 ).build();

    }
}

Ответ 13

Используя Java 8 lambdas, вы можете приблизиться к реальным именованным параметрам.

foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});

Заметьте, что это, вероятно, нарушает пару десятков "лучших практик Java" (например, все, что использует символ $).

public class Main {
  public static void main(String[] args) {
    // Usage
    foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
    // Compare to roughly "equivalent" python call
    // foo(foo = -10, bar = "hello", array = [1, 2, 3, 4])
  }

  // Your parameter holder
  public static class $foo {
    private $foo() {}

    public int foo = 2;
    public String bar = "test";
    public int[] array = new int[]{};
  }

  // Some boilerplate logic
  public static void foo(Consumer<$foo> c) {
    $foo foo = new $foo();
    c.accept(foo);
    foo_impl(foo);
  }

  // Method with named parameters
  private static void foo_impl($foo par) {
    // Do something with your parameters
    System.out.println("foo: " + par.foo + ", bar: " + par.bar + ", array: " + Arrays.toString(par.array));
  }
}

Плюсы:

  • Значительно короче любого шаблона построителя, который я видел до сих пор.
  • Работает как для методов, так и для конструкторов.
  • Полностью безопасный тип
  • Он выглядит очень близко к фактическим именованным параметрам в других языках программирования.
  • Это примерно так же безопасно, как и ваш типичный шаблон построителя (может задавать параметры несколько раз)

Минусы:

  • Ваш босс, вероятно, будет линчевать вас за это
  • Сложнее сказать, что происходит

Ответ 14

Любое решение в Java, вероятно, будет довольно многословным, но стоит упомянуть, что такие инструменты, как Google AutoValues ​​ и Immutables будет генерировать классы-конструкторы для автоматического использования обработки аннотации компиляции JDK.

В моем случае я хотел, чтобы именованные параметры использовались в перечислении Java, поэтому шаблон компоновщика не работал, потому что экземпляры enum не могут быть созданы другими классами. Я придумал подход, похожий на @deamon, но добавляет проверку времени упорядочения параметров (за счет большего количества кода) во время компиляции

Здесь код клиента:

Person p = new Person( age(16), weight(100), heightInches(65) );

И реализация:

class Person {
  static class TypedContainer<T> {
    T val;
    TypedContainer(T val) { this.val = val; }
  }
  static Age age(int age) { return new Age(age); }
  static class Age extends TypedContainer<Integer> {
    Age(Integer age) { super(age); }
  }
  static Weight weight(int weight) { return new Weight(weight); }
  static class Weight extends TypedContainer<Integer> {
    Weight(Integer weight) { super(weight); }
  }
  static Height heightInches(int height) { return new Height(height); }
  static class Height extends TypedContainer<Integer> {
    Height(Integer height) { super(height); }
  }

  private final int age;
  private final int weight;
  private final int height;

  Person(Age age, Weight weight, Height height) {
    this.age = age.val;
    this.weight = weight.val;
    this.height = height.val;
  }
  public int getAge() { return age; }
  public int getWeight() { return weight; }
  public int getHeight() { return height; }
}