Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are C# style object initializers available in Java

Tags:

java

Like this one? 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"
    }
};
like image 330
Hao Avatar asked Feb 02 '12 07:02

Hao


People also ask

Are C and C++ the same?

While both C and C++ may sound similar, their features and usage are different. C is a procedural programming language and does not support objects and classes. C++ is an enhanced version of C programming with object-oriented programming support.

What is meant by C?

noun plural c's, C's or Cs. the third letter and second consonant of the modern English alphabet. a speech sound represented by this letter, in English usually either a voiceless alveolar fricative, as in cigar, or a voiceless velar stop, as in case.

Is there a C+?

C+ (grade), an academic grade. C++, a programming language. C with Classes, predecessor to the C++ programming language. ANSI C, a programming language (as opposed to K&R C)


4 Answers

Actually, there is!

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

or even:

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

This is called double brace initialization. However I would avoid this idiom as it has some unexpected side-effects, e.g. this syntax actually creates an anonymous inner class Person$1 and Address$.

See also

  • What is Double Brace initialization in Java?
  • Double Brace Initialization
like image 189
Tomasz Nurkiewicz Avatar answered Oct 01 '22 11:10

Tomasz Nurkiewicz


Others have shown the "double brace" initializers, which I think should be avoided - this isn't what inheritance is for, and it will only work as shown when the fields are directly visible to subclasses, which I'd also argue against. It's not really the same thing as C# initializer blocks. It's a hack to take advantage of a language feature designed for other purposes.

If you have more values than you wish to pass to a constructor, you might want to consider using the builder pattern:

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

This also allows you to make Person immutable. On the other hand, doing this requires the Person class to be designed for this purpose. That's nice for autogenerated classes (it's the pattern that Protocol Buffers follows) but is annoying boiler-plate for manually-written code.

like image 33
Jon Skeet Avatar answered Oct 01 '22 10:10

Jon Skeet


Normally we use constructors in java to such cases

by using a constructor in the class which you want to create an object you can use that to pass the arguments at the object creating step, ex- MyData obj1 = new MyData("name",24);

for this case you have to use parameterized constructor matching to the arguments you pass from the main method

Ex-

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

The full code as follows

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

    }
}
like image 44
Chamith Chathuka Avatar answered Oct 01 '22 12:10

Chamith Chathuka


Since double curly braces are generally avoided, you can create a very simple and generic sort of "builder" class that can set properties in a somewhat idiomatic way.

Note: I call the class "Bean" or POJO to follow the javabean standard: What is a JavaBean exactly?. I would primarily use this class to init javabeans anyway.

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

Instances of this Bean class can be created from an existing object or generated using a Supplier. The object is stored in the the field object. The set method is a higher-order function that takes in another function--Consumer<T>. Consumers take in one argument and return void. This will create the setter side-effects in a new scope.

The Bean .set(...) method returns object that can be used directly in an assignment.

I like this method because the object's assignment are contained within closed blocks and it feels like I'm setting properties before the object is created rather than than creating the object and mutating it.

The end result is a decent way to create new java objects but this is still a little wordy from the C# object initializer sigh.


And here is the class in use:

    // '{}' 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
    });

if you have nested items, it might be a better to name the variables accordingly. Java doesn't let you use reuse $ because it exists in the outer scope and there is no shadowing.

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

now compare the normal code

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

Alternatively, you could have a lambda that takes in void and returns your object and cast it as a Supplier<DesiredType> and immediately invoke .get(). This doesn't require a separate class but you have to create bean yourself.

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

A note on practicality: Because you can't reuse $ when nesting elements, this method still tends to be a bit wordy. The variable names start getting long and the any syntax appeal goes away.

It can also be easy to abuse the set() method to create instance of objects within the closure. To use correctly, the only side affects should be on the object you're creating.

One more note: this is really just for fun. Don't ever use this in production.

like image 39
Rico Kahler Avatar answered Oct 01 '22 10:10

Rico Kahler