简体   繁体   English

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

[英]Are C# style object initializers available in Java

Like this one?像这个? http://weblogs.asp.net/dwahlin/archive/2007/09/09/c-3-0-features-object-initializers.aspx 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"
    }
};

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, eg this syntax actually creates an anonymous inner class Person$1 and Address$ .但是我会避免这种习惯用法,因为它有一些意想不到的副作用,例如这种语法实际上创建了一个匿名内部类Person$1Address$

See also也可以看看

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.它与 C# 初始值设定项块并不是一回事。 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.这也允许您使Person不可变。 On the other hand, doing this requires the Person class to be designed for this purpose.另一方面,这样做需要为此目的设计Person类。 That's nice for autogenerated classes (it's the pattern that Protocol Buffers follows) but is annoying boiler-plate for manually-written code.这对于自动生成的类(这是Protocol Buffers遵循的模式)很好,但对于手动编写的代码来说却是烦人的样板。

Normally we use constructors in java to such cases通常我们在java中使用构造函数来处理这种情况

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);通过在要创建对象的类中使用构造函数,您可以使用它在对象创建步骤传递参数,例如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对于这种情况,您必须使用与从 main 方法传递的参数匹配的参数化构造函数

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

    }
}

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?注意:我将类称为“Bean”或 POJO 以遵循 javabean 标准: JavaBean 究竟是什么? . . I would primarily use this class to init javabeans anyway.无论如何,我主要使用这个类来初始化 javabeans。

Bean.java 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.此 Bean 类的实例可以从现有对象创建或使用供应商生成。 The object is stored in the the field object .该对象存储在字段object The set method is a higher-order function that takes in another function-- Consumer<T> . set 方法是一个高阶函数,它接收另一个函数—— Consumer<T> Consumers take in one argument and return void.消费者接受一个论点并返回无效。 This will create the setter side-effects in a new scope.这将在新范围内创建 setter 副作用。

The Bean .set(...) method returns object that can be used directly in an assignment..set(...)方法返回object ,可以直接在分配中使用。

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 .最终的结果是创建新的Java对象一个体面的方式,但是这仍然是从C#对象有点罗嗦初始化叹息


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

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() .或者,您可以有一个 lambda 接受 void 并返回您的对象并将其转换为Supplier<DesiredType>并立即调用.get() This doesn't require a separate class but you have to create bean yourself.这不需要单独的类,但您必须自己创建 bean。

    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.滥用 set() 方法在闭包中创建对象的实例也很容易。 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.永远不要在生产中使用它。

If your classes have constructors that takes values for the members, you can create the instance like this:如果您的类具有为成员取值的构造函数,您可以像这样创建实例:

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

If not, you have to use the setter methods after object creation.如果没有,则必须在创建对象后使用 setter 方法。

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

Take a look at the official Java tutorial .看看官方的 Java 教程

No, there isn't.不,没有。 There are some hacks and workarounds but Java does not have object initializers.有一些技巧和变通方法,但 Java 没有对象初始值设定项。 The basic premise is... Use double brace initialization which syntactically looks similar but is not the same thing under the hood.基本前提是......使用双括号初始化,它在语法上看起来相似但在引擎盖下并不相同。 Or create constructors that you can use to initialize the object (the old way).或者创建可用于初始化对象的构造函数(旧方法)。 Or go nut job crazy and use design patterns like builder etc. to do something that is built into C#.或者疯狂地工作,使用诸如构建器等设计模式来做一些内置于 C# 的事情。

You can do something similar in Java with a double brace initialization block:您可以使用双括号初始化块在 Java 中执行类似的操作:

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

However this is just using an initialization block inside an anonymous inner class so would be less efficient than constructing the objects in the normal way.然而,这只是在匿名内部类中使用初始化块,因此效率低于以正常方式构造对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM