Pregunta ¿Están disponibles los inicializadores de objetos de estilo C # en Java?


¿Como éste? http://weblogs.asp.net/dwahlin/archive/2007/09/09/c-3-0-features-object-initializers.aspx

Person p = new Person()
{
    FirstName = "John",
    LastName = "Doe",
    Address = new Address()
    {
        Street = "1234 St.",
        City = "Phoenix"
    }
};

34
2018-02-02 07:22


origen


Respuestas:


En realidad, ¡hay!

Person p = new Person()
{{
    setFirstName("John");
    setLastName("Doe");
    setAddress(new Address()
    {{
        setStreet("1234 St.");
        setCity("Phoenix");
    }});
}};

o incluso:

Person p = new Person()
{{
    firstName = "John";
    lastName = "Doe";
    address = new Address()
    {{
        street = "1234 St.";
        city = "Phoenix";
    }});
}};

Se llama inicialización doble del paréntesis. Sin embargo, evitaría este modismo ya que tiene algunos efectos secundarios inesperados, p. esta sintaxis en realidad crea una clase interna anónima Person$1 y Address$.

Ver también


52
2018-02-02 07:25



Otros han mostrado los inicializadores de "doble llave", que creo que deberían evitarse: para eso no es herencia, y solo funcionará como se muestra cuando los campos son directamente visibles para las subclases, lo que también discutiría. No es realmente lo mismo que los bloques iniciales de C #. Es un truco para aprovechar una función de idioma diseñada para otros fines.

Si tiene más valores de los que desea pasar a un constructor, le recomendamos que considere usar el patrón del constructor:

Person person = Person.newBuilder()
    .setFirstName("John")
    .setLastName("Doe")
    .setAddress(Address.newBuilder()
        .setStreet("...")
        .setCity("Phoenix")
        .build())
    .build();

Esto también te permite hacer Person inmutable. Por otro lado, hacer esto requiere Person clase que se diseñará para este propósito. Eso es bueno para las clases autogeneradas (es el patrón que Buffers de protocolo sigue) pero es una placa de caldera molesta para el código escrito manualmente.


25
2018-02-02 07:35



Dado que generalmente se evitan las llaves dobles, puede crear una clase de "constructor" muy simple y genérica que pueda establecer propiedades de una manera un tanto idiomática.

Nota: Llamo a la clase "Bean" o POJO para seguir el estándar javabean: ¿Qué es un JavaBean exactamente?. Principalmente usaría esta clase para iniciar javabeans de todos modos.

Bean.java

public class Bean<T> {
    private T object;
    public Bean(Supplier<T> supplier) { object = supplier.get(); }
    public Bean(T object) { this.object = object; }
    public T set(Consumer<T> setter) {
        setter.accept(object);
        return object;
    }
}

Las instancias de esta clase de Bean se pueden crear a partir de un objeto existente o se pueden generar utilizando un Proveedor. El objeto se almacena en el campo object. El método set es una función de orden superior que tiene otra función--Consumer<T>. Los consumidores toman en un argumento y devuelven el vacío. Esto creará los efectos secundarios del colocador en un nuevo ámbito.

El frijol .set(...) método devuelve object que se puede usar directamente en una tarea.

Me gusta este método porque la asignación del objeto está dentro de bloques cerrados y siente como si estuviera configurando propiedades antes de crear el objeto en lugar de crear el objeto y mutarlo.

El resultado final es una forma decente de crear nuevos objetos Java, pero esto todavía es un poco prolijo desde el inicializador de objetos C # suspiro.


Y aquí está la clase en uso:

    // '{}' creates another scope so this function's scope is not "polluted"
    // '$' is used as the identifier simply because it's short
    Rectangle rectangle = new Bean<>(Rectangle::new).set($ -> {
        $.setLocation(0, 0);
        $.setBounds(0, 0, 0, 0);
        // set other properties
    });

si tiene elementos anidados, podría ser mejor nombrar las variables en consecuencia. Java no te permite usar la reutilización $ porque existe en el ámbito externo y no hay sombras.

    // this time we pass in new Rectangle() instead of a Supplier
    Rectangle rectangle3 = new Bean<>(new Rectangle()).set(rect-> {
        rect.setLocation(-50, -20);
        // setBounds overloads to take in another Rectangle
        rect.setBounds(new Bean<>(Rectangle::new).set(innerRect -> {
            innerRect.setLocation(0, 0);
            innerRect.setSize(new Bean<>(Dimension::new).set(dim -> {
                dim.setSize(640, 480);
            }));
        }));
    });

ahora compara el código normal

    // innerRect and dimension are part of the outer block's scope (bad)
    Rectangle rectangle4 = new Rectangle();
    rectangle4.setLocation(-50, -20);
    Rectangle innerRect = new Rectangle();
    innerRect.setLocation(0, 0);
    Dimension dimension = new Dimension();
    dimension.setSize(640, 480);
    innerRect.setSize(dimension);
    rectangle4.setBounds(innerRect);

Alternativamente, podrías tener un lambda que se anula y devuelve tu objeto y lo lanza como un Supplier<DesiredType> e inmediatamente invocar .get(). Esto no requiere una clase separada, pero tienes que crear bean tú mismo.

    Rectangle rectangle5 = ((Supplier<Rectangle>)() -> {
        Rectangle rect = new Rectangle();
        rect.setLocation(0, 0);
        return rect;
    }).get();

Una nota sobre practicidad: Porque no puedes reutilizar $ cuando se anidan elementos, este método aún tiende a ser un poco prolijo. Los nombres de las variables empiezan a ser largos y el atractivo de sintaxis desaparece.

También puede ser fácil abusar del método set () para crear una instancia de objetos dentro del cierre. Para usar correctamente, los únicos efectos secundarios deben estar en el objeto que está creando.

Una nota más: esto es realmente solo por diversión. Nunca use esto en producción.


4
2018-06-01 01:44



Normalmente usamos constructores en java para tales casos

mediante el uso de un constructor en la clase en la que desea crear un objeto, puede usarlo para pasar los argumentos en el paso de creación del objeto, ex- MyData obj1 = new MyData("name",24);

para este caso, debe usar la coincidencia de constructor parametrizado con los argumentos que pasa del método principal

Ex-

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

El código completo de la siguiente manera

class MyData{
public String name;
public int age;

 MyData(String name, int age){
    this.name=name;
    this.age=age;
    }
     public static void main(String args[]){
        MyData obj1 = new MyData("name",24);

    }
}

3
2018-02-05 17:52



Si sus clases tienen constructores que toman valores para los miembros, puede crear la instancia de esta manera:

Person p = new Person("John", "Doe", new Address("1234 St.", "Phoenix"));

De lo contrario, debe usar los métodos setter después de la creación del objeto.

Person p = new Person();
p.setFirstName("John");
// and so on

Eche un vistazo a la tutorial oficial de Java.


2
2018-02-02 07:25



Puede hacer algo similar en Java con un bloque de inicialización de doble llave:

Person p = new Person() {{
    firstName = "John";
    lastName = "Doe";
    address = new Address() {{
        street = "1234 St.";
        city = "Phoenix";
    }};
}};

Sin embargo, esto solo es usar un bloque de inicialización dentro de una clase interna anónima, por lo que sería menos eficiente que construir los objetos de la manera normal.


1
2018-02-02 07:28