Java中是否提供C#样式对象初始值设定项

Are C# style object initializers available in Java

like this one?http:/ / / / / weblogs.asp.net dwahlin馆2007年09 / / / c-3-0-features-object-initializers.aspx 09P></

1
2
3
4
5
6
7
8
9
10
Person p = new Person()
{
    FirstName ="John",
    LastName ="Doe",
    Address = new Address()
    {
        Street ="1234 St.",
        City ="Phoenix"
    }
};

事实上,有!

1
2
3
4
5
6
7
8
9
10
Person p = new Person()
{{
    setFirstName("John");
    setLastName("Doe");
    setAddress(new Address()
    {{
        setStreet("1234 St.");
        setCity("Phoenix");
    }});
}};

甚至:

1
2
3
4
5
6
7
8
9
10
Person p = new Person()
{{
    firstName ="John";
    lastName ="Doe";
    address = new Address()
    {{
        street ="1234 St.";
        city ="Phoenix";
    }});
}};

这称为双括号初始化。但是,我会避免使用这个成语,因为它有一些意想不到的副作用,例如,这个语法实际上创建了一个匿名的内部类Person$1Address$

也见

  • Java中双括号初始化是什么?
  • 双括号初始化


其他人展示了"双大括号"初始值设定项,我认为应该避免这一点——这不是继承的目的,它只会在字段对子类直接可见时才能工作,我也反对这一点。它与C初始值设定项块实际上不同。利用为其他目的设计的语言特性是一种黑客行为。

如果您的值超过了要传递给构造函数的数量,那么您可能需要考虑使用构建器模式:

1
2
3
4
5
6
7
8
Person person = Person.newBuilder()
    .setFirstName("John")
    .setLastName("Doe")
    .setAddress(Address.newBuilder()
        .setStreet("...")
        .setCity("Phoenix")
        .build())
    .build();

这还允许您使Person不可变。另一方面,这样做需要为此目的设计Person类。这对于自动生成的类(协议缓冲区遵循的模式)很好,但是对于手动编写的代码来说,这是一个令人恼火的问题。


由于通常避免使用双大括号,因此可以创建一个非常简单和通用的"builder"类,该类可以以某种惯用方式设置属性。

注意:我调用类"bean"或pojo来遵循JavaBean标准:JavaBean到底是什么?.I主要使用这个类初始化JavaBeans。

爪哇

1
2
3
4
5
6
7
8
9
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;
    }
}

这个be an类的实例可以从现有对象创建,也可以使用供应商生成。对象存储在字段object中。set方法是一个高阶函数,它接受另一个函数——Consumer。消费者接受一个论据,并返回无效。这将在新范围内创建setter副作用。

be an .set(...)方法返回可以直接在赋值中使用的object

我喜欢这个方法,因为对象的赋值包含在封闭的块中,感觉就像是在创建对象之前设置属性,而不是创建对象并改变它。

最终的结果是创建新Java对象的一种体面的方法,但从C对象初始化器的叹息来看,这仍然有点冗长。

下面是正在使用的类:

1
2
3
4
5
6
7
    // '{}' 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
    });

如果您有嵌套项,那么最好对变量进行相应的命名。Java不允许使用重用EDCOX1(4),因为它存在于外部范围内并且没有阴影。

1
2
3
4
5
6
7
8
9
10
11
    // 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);
            }));
        }));
    });

现在比较正常代码

1
2
3
4
5
6
7
8
9
    // 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);

或者,您可以有一个lambda,它接收空的并返回对象,并将其转换为Supplier,然后立即调用.get()。这不需要单独的类,但您必须自己创建bean。

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

关于实用性的一个注意事项:因为嵌套元素时不能重用$,所以这个方法仍然有点冗长。变量名开始变长,任何语法吸引力都消失了。

也很容易滥用set()方法在闭包中创建对象的实例。要正确使用,唯一的副作用应该是创建的对象。

另一个注意事项:这真的只是为了好玩。不要在生产中使用这个。


通常我们使用Java中的构造函数来处理这种情况。

通过在要创建对象的类中使用构造函数,可以使用该构造函数在对象创建步骤传递参数,除MyData obj1 = new MyData("name",24);

对于这种情况,必须使用与从主方法传递的参数匹配的参数化构造函数。

前-

1
2
3
4
MyData(String name, int age){
    this.name=name;
    this.age=age;
    }

完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
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);

    }
}

如果类具有为成员获取值的构造函数,则可以这样创建实例:

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

如果不是,则必须在创建对象后使用setter方法。

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

看看官方的Java教程。


您可以在Java中用双括号初始化块进行类似的操作:

1
2
3
4
5
6
7
8
Person p = new Person() {{
    firstName ="John";
    lastName ="Doe";
    address = new Address() {{
        street ="1234 St.";
        city ="Phoenix";
    }};
}};

但是,这只是在匿名内部类中使用初始化块,因此比以正常方式构造对象的效率要低。