有没有办法在调用另一个构造函数时访问this.toString()的值?

时间:2014-02-03 12:31:42

标签: java methods constructor instance

对于谈论对象处于“单元化状态”这一事实的每个人,请参阅the answer to this question,其中显示对象引用可以被传递,解除引用,从中调用方法,以及在构造函数终止之前访问字段并且已分配所有字段(包括 final字段)。

所以这是用例:

public class Entity {

    private final String name;

    public Entity() {
        this(toString()); //Nope, Chuck Testa
    }

    public Entity(String name) {
        this.name = name;
    }
}

编译错误是:

  

在显式调用构造函数时无法引用实例方法。

请注意,toString()尚未被覆盖,并且是来自Object的默认电话。

我当然对这背后的哲学/技术原因感兴趣,所以如果有人能解释,那将是一个很棒的奖金。但是我正在寻找一种从默认构造函数中调用toString()的方法,因为它引用了具有更多参数的更具体的构造函数。实际的用例有点复杂,最后一直指向一个带有四个参数的构造函数,但这并不重要。

我知道我可以做这样的事......

private static final String TO_STRING_CONSTRUCTOR_ARGUMENT = "aflhsdlkfjlkswf";

public Entity() {
    this(TO_STRING_CONSTRUCTOR_ARGUMENT);
}

public Entity(String name) {
    this.name = name == TO_STRING_CONSTRUCTOR_ARGUMENT ? toString() : name;
}

......但它似乎是一个非常不优雅的解决方案。

那么,有什么方法可以把它拉下来?或者任何建议的最佳做法来处理这种情况?

5 个答案:

答案 0 :(得分:2)

我希望在创建对象之前不要传递this。相反,我会这样做:

public class Entity {

    private final String name;

    public Entity() {
        this(null); // or whatever
    }

    public Entity(String name) {
        this.name = name;
    }

    public String getName() { 
        return name != null ? name : Objects.hashCode(this);
    }
}

如果您可以在没有最终name的情况下生活,则可以使用初始化程序块:

public class Entity {

    private String name;

    {name = this.toString();}

    public Entity() {
    }

    public Entity(String name) {
        this.name = name;
    }
}

this仅在完成对this()super()的所有通话后才可用。构造函数调用super()后,初始化程序首先运行,并允许访问this

答案 1 :(得分:1)

至于编译错误的原因,请参阅JLS的section 8.8.7。造成编译器错误的原因尚不清楚,但考虑到构造函数链必须是new对象时执行的第一件事,并在此处查看评估顺序:

public Entity() {
        this(toString()); 
}
在调用超级构造函数之前,首先计算

toString()。总的来说,这为未初始化状态留下了各种可能性。

作为个人偏好,我建议在构造函数中可以使用对象创建有效状态所需的所有内容。如果在没有调用对象层次结构中定义的其他方法的情况下无法在默认构造函数中提供有效状态,那么请删除默认构造函数并将该函数放在类的用户身上,以便为其他构造函数提供有效的String。

如果您最终只是尝试使用toString()的值调用其他构造函数,那么我建议使用以下内容:

    public Entity() {
        name = toString();
    }

实现了与实现和正确初始化名称相同的目标。

答案 2 :(得分:1)

JLS中所述,在初始化实例之前不允许这样做。

但是,有一些方法可以一致的方式处理您的场景。

在我看到您的情况时,您希望表示生成的值(toString())或用户提供的值,该值可以为null。

鉴于这种限制,使用TO_STRING_CONSTRUCTOR_ARGUMENT对于至少一个特定用例来说是失败的,但它可能是模糊的。

基本上,您需要使用类似于Google Guava中存在的可选项替换String,并且将包含在Java 8中,并且可以在许多其他语言中看到。

拥有StringOptional / StringHolder或您选择的任何内容,类似于:

public class StringOptional {
  private String value;
  private boolean set = false;
  public StringOptional() {}
  public StringOptional(String value) {
    this.value = value;
    this.set = true;
  }

  public boolean isSet() { return set; }
  public String getValue() { return value; }
}

然后,您可以使用推断路径的知识调用构造函数。

public class Entity {
   public Entity() { 
     this(New StringOptional());
   }

   public Entity(String s) {
     this(new StringOptional(s));
   }

   private Entity(StringOptional optional) {
     super(optional);
   }
}

并将其存储为次要需求:

if (optional.isSet() ? optional.getValue() : toString();

这就是我通常会处理一个可能为空的场景的方式,希望它可以作为答案加以增加。

答案 3 :(得分:0)

您可以在类Entity中使用静态方法工厂,并将构造函数设为私有:

public class Entity {

    private String name;

    private Entity() {
    }

    public Entity(String name) {
        this.name = name;
    }

    public static Entity createEntity() {
        Entity result = new Entity();
        result.name = result.toString();
        return result;
    }
}

答案 4 :(得分:0)

您无法“使用”尚未创建的实例。通过调用第二个构造函数来推迟创建,您不能在调用之前或在调用操作中使用它。