Java 多个构造函数,某些参数没有默认参数值

Java 多个构造函数,某些参数没有默认参数值,java,constructor,Java,Constructor,如果您需要创建多个构造函数,直接的解决方案将是链接这些构造函数。一个构造函数使用一些默认值调用另一个构造函数。 但是如果没有默认值呢 例如,具有以下三个构造函数的类: public MyClass(Person person, SomeEnum enum1) // constructor 1 { if (person== null) { throw new IllegalArgumentException("person cannot be null.");

如果您需要创建多个构造函数,直接的解决方案将是链接这些构造函数。一个构造函数使用一些默认值调用另一个构造函数。 但是如果没有默认值呢

例如,具有以下三个构造函数的类:

public MyClass(Person person, SomeEnum enum1) // constructor 1
{

    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }
    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.person= person;
    this.enum1 = enum1;     
}

public MyClass(Person person)  // constructor 2
{

    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }   

    this.person= person;    
}

public MyClass(SomeEnum enum1)  // constructor 3
{

    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.enum1 = enum1;     
}
我不想在多个构造函数中验证相同的字段,因此我将尝试让构造函数2和3调用构造函数1:

假设我们在SomeEnum中有一些默认值,但是对于构造函数3,我没有Person的默认值。 我可以更改调用链,让构造函数1和2调用构造函数3,然后自己验证和设置Person,但我不希望Person验证出现在两个地方

我也不喜欢创建一些NotPerson对象来扩展Person并使用它。 构建器模式也不适合我。
此外,验证可能比检查null更复杂。

一种方法是编写私有静态验证方法。使用它们的紧凑方式如下所示:

public MyClass(Person person, SomeEnum enum1) {
    this.person = validatePerson(person);
    this.enum1 = validateSomeEnum(enum1);
}
public MyClass(Person person) {
    this.person = validatePerson(person);
}
public MyClass(SomeEnum enum1) {
    this.enum1 = validateSomeEnum(enum1);
}

private static Person validatePerson(Person person) {
    if (person == null) {
        throw new IllegalArgumentException("person cannot be null.");
    }
    return person;
}
private static SomeEnum validateSomeEnum(SomeEnum enum1) {
    if (enum1 == null) {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }
    return enum1;
}
使用


您还可以使用静态构造方法来实现这一点,通过对静态方法进行不同的命名,有助于澄清存在歧义的参数:

public static MyClass of(Person person, SomeEnum enum1) {
    validatePerson(person);
    validateSomeEnum(enum1);
    return new MyClass(person, enum1);
}
public static MyClass of(Person person) {
    validatePerson(person);
    return new MyClass(person, null);
}
public static MyClass of(SomeEnum enum1) {
    validateSomeEnum(enum1);
    return new MyClass(null, enum1);
}

private static void validatePerson(Person person) {
    if (person == null) {
        throw new IllegalArgumentException("person cannot be null.");
    }
}
private static void validateSomeEnum(SomeEnum enum1) {
    if (enum1 == null) {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }
}

private MyClass(Person person, SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;
}
使用


另一个选项是生成器模式,如果有许多可选属性,则该模式尤其有用:

public static final class Builder {
    private Person person;
    private SomeEnum enum1;

    public Builder withPerson(Person person) {
        if (person == null) {
            throw new IllegalArgumentException("person cannot be null.");
        }
        this.person = person;
        return this;
    }
    public Builder withSomeEnum(SomeEnum enum1) {
        if (enum1 == null) {
            throw new IllegalArgumentException("enum1 cannot be null.");
        }
        this.enum1 = enum1;
        return this;
    }
    public MyClass create() {
        if (this.person == null && this.enum1 == null) {
            throw new IllegalArgumentException("One of person or enum1 is required.");
        }
        return new MyClass(this.person, this.enum1);
    }
}

private MyClass(Person person, SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;
}
使用


如果您的问题主要是重复检查,则可以轻松解决此问题。它将自动生成等效代码。默认情况下,它使用NullPointerException,但如果需要,可以配置为使用IllegalArgumentException

public MyClass(@NonNull Person person, @NonNull SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;     
}

public MyClass(@NonNull Person person) {
    this.person = person;    
}

public MyClass(@NonNull SomeEnum enum1) {
    this.enum1 = enum1;     
}

另一个解决方案是更好地重新安排链调用:

public MyClass(Person person, SomeEnum enum1) // constructor 1
{

    this(enum1);
    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }   

    this.person= person;   
}

public MyClass(Person person)  // constructor 2
{
    this(person, SomeEnum.NoValue);     
}

public MyClass(SomeEnum enum1)  // constructor 3
{

    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.enum1 = enum1;     
}

将验证提取到方法中?在第一个示例构造函数2中,构造后字段
enum1
的状态应该是什么?无效的同样地,第一个示例构造函数3,字段
person
的状态应该是什么?如果必须提供两个参数,则两个单参数构造函数都不可能是合法的,除非它们都有合理的默认值,您没有提到。你的问题没有意义。
this(null,enum1)?我不认为验证应该是不同的c'tor(我的意思是验证逻辑,而不是方法),因此-在第三个c'tor中,您不应该将person保留为null,因为根据第一个c'tor它是无效的。问题很简单-person的默认值是什么-如果为null,第一个不要失败,如果是其他的-在第三个中使用它。一旦你解决了这个问题,使用第一个c'tor(或构建器)就是ClearThanks,我认为第一个解决方案是三者中最好的,因为在我的例子中,我有很多类扩展了MyClass,而不是使用super来调用不同MyClass的构造函数。
public static final class Builder {
    private Person person;
    private SomeEnum enum1;

    public Builder withPerson(Person person) {
        if (person == null) {
            throw new IllegalArgumentException("person cannot be null.");
        }
        this.person = person;
        return this;
    }
    public Builder withSomeEnum(SomeEnum enum1) {
        if (enum1 == null) {
            throw new IllegalArgumentException("enum1 cannot be null.");
        }
        this.enum1 = enum1;
        return this;
    }
    public MyClass create() {
        if (this.person == null && this.enum1 == null) {
            throw new IllegalArgumentException("One of person or enum1 is required.");
        }
        return new MyClass(this.person, this.enum1);
    }
}

private MyClass(Person person, SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;
}
MyClass myClass = new MyClass.Builder()
        .withPerson(person)
        .withSomeEnum(enum1)
        .create();
MyClass myClass = new MyClass.Builder()
        .withPerson(person)
        .create();
MyClass myClass = new MyClass.Builder()
        .withSomeEnum(enum1)
        .create();
public MyClass(@NonNull Person person, @NonNull SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;     
}

public MyClass(@NonNull Person person) {
    this.person = person;    
}

public MyClass(@NonNull SomeEnum enum1) {
    this.enum1 = enum1;     
}
public MyClass(Person person, SomeEnum enum1) // constructor 1
{

    this(enum1);
    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }   

    this.person= person;   
}

public MyClass(Person person)  // constructor 2
{
    this(person, SomeEnum.NoValue);     
}

public MyClass(SomeEnum enum1)  // constructor 3
{

    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.enum1 = enum1;     
}