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

Hao*_*Hao 46 java

像这个?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"
    }
};
Run Code Online (Sandbox Code Playgroud)

Tom*_*icz 66

实际上,有!

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

甚至:

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

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

也可以看看

  • 当然,这也要求您的字段仅受保护.虽然表面上的两种形式*看起来相似,但它们确实非常不同. (10认同)

Jon*_*eet 29

其他人已经展示了"双支撑"初始化器,我认为应该避免这种情况 - 这不是继承的用途,它只会在字段直接对子类可见时显示,我也反对.它与C#初始化程序块不是一回事.利用为其他目的设计的语言功能是一种破解.

如果您拥有的值多于希望传递给构造函数的值,则可能需要考虑使用构建器模式:

Person person = Person.newBuilder()
    .setFirstName("John")
    .setLastName("Doe")
    .setAddress(Address.newBuilder()
        .setStreet("...")
        .setCity("Phoenix")
        .build())
    .build();
Run Code Online (Sandbox Code Playgroud)

这也允许你使Person不可变.另一方面,这样做需要Person为此目的设计类.这对于自动生成的类(它是Protocol Buffers遵循的模式)很好,但是对于手动编写的代码来说是令人讨厌的样板.

  • 这听起来很奇怪,但为什么不通过返回`this`链接`set`函数?然后你可以编写`new Person().setFirstName("John").setLastName("Skeet")`. (2认同)
  • @ ashes999:你可以做 - 但这很奇怪.(当然,它也不适用于不变性.) (2认同)

Cha*_*uka 5

通常我们在java中使用构造函数来处理这种情况

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

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

前任-

MyData(String name, int age){
    this.name=name;
    this.age=age;
    }
Run Code Online (Sandbox Code Playgroud)

完整代码如下

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

    }
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*ler 5

由于通常避免使用双花括号,您可以创建一个非常简单和通用的“构建器”类,它可以以某种惯用的方式设置属性。

注意:我将类称为“Bean”或 POJO 以遵循 javabean 标准:JavaBean 究竟是什么?. 无论如何,我主要使用这个类来初始化 javabeans。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Bean.set(...)方法返回object可直接在赋值中使用的方法。

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

最终的结果是创建新的Java对象一个体面的方式,但是这仍然是从C#对象有点罗嗦初始化感叹


这是正在使用的类:

    // '{}' 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
    });
Run Code Online (Sandbox Code Playgroud)

如果您有嵌套的项目,则相应地命名变量可能会更好。Java 不允许您使用重用,$因为它存在于外部作用域中并且没有阴影。

    // 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);
            }));
        }));
    });
Run Code Online (Sandbox Code Playgroud)

现在比较正常的代码

    // 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);
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用一个 lambda 来接收 void 并返回您的对象并将其转换为 aSupplier<DesiredType>并立即调用.get()。这不需要单独的类,但您必须自己创建 bean。

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

关于实用性的一个说明:因为$嵌套元素的时候不能复用,所以这个方法还是比较啰嗦的。变量名开始变长,任何语法吸引力都消失了。

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

还有一点要注意:这真的只是为了好玩。永远不要在生产中使用它。