为什么必须首先在Java构造函数中委托给不同的构造函数? [英] Why must delegation to a different constructor happen first in a Java constructor?

查看:102
本文介绍了为什么必须首先在Java构造函数中委托给不同的构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Java的构造函数中,如果要调用另一个构造函数(或超级构造函数),它必须是构造函数中的第一行。我假设这是因为在其他构造函数运行之前不应该允许修改任何实例变量。但是为什么你不能在构造函数委托之前有语句,以便计算其他函数的复杂值?我想不出任何好的理由,而且我遇到了一些真实的案例,我写了一些丑陋的代码来解决这个限制。

In a constructor in Java, if you want to call another constructor (or a super constructor), it has to be the first line in the constructor. I assume this is because you shouldn't be allowed to modify any instance variables before the other constructor runs. But why can't you have statements before the constructor delegation, in order to compute the complex value to the other function? I can't think of any good reason, and I have hit some real cases where I have written some ugly code to get around this limitation.

所以我只是想知道:


  1. 有这个限制的充分理由吗?

  2. 是否有任何计划允许这在未来的Java版本中? (或者Sun明确表示不会发生这种情况?)






我正在谈论的例子,考虑一下我写的一些代码,我在此StackOverflow答案。在那段代码中,我有一个BigFraction类,它有一个BigInteger分子和一个BigInteger分母。 规范构造函数是 BigFraction(BigInteger分子,BigInteger分母)表单。对于所有其他构造函数,我只是将输入参数转换为BigIntegers,并调用规范构造函数,因为我不想复制所有工作。


For an example of what I'm talking about, consider some code I wrote which I gave in this StackOverflow answer. In that code, I have a BigFraction class, which has a BigInteger numerator and a BigInteger denominator. The "canonical" constructor is the BigFraction(BigInteger numerator, BigInteger denominator) form. For all the other constructors, I just convert the input parameters to BigIntegers, and call the "canonical" constructor, because I don't want to duplicate all the work.

在某些情况下,这很容易;例如,带两个 long s的构造函数是微不足道的:

In some cases this is easy; for example, the constructor that takes two longs is trivial:

  public BigFraction(long numerator, long denominator)
  {
    this(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
  }

但在其他情况下,更难。考虑采用BigDecimal的构造函数:

But in other cases, it is more difficult. Consider the constructor which takes a BigDecimal:

  public BigFraction(BigDecimal d)
  {
    this(d.scale() < 0 ? d.unscaledValue().multiply(BigInteger.TEN.pow(-d.scale())) : d.unscaledValue(),
         d.scale() < 0 ? BigInteger.ONE                                             : BigInteger.TEN.pow(d.scale()));
  }

我发现这很难看,但它可以帮助我避免重复代码。以下是我想要做的,但它在Java中是非法的:

I find this pretty ugly, but it helps me avoid duplicating code. The following is what I'd like to do, but it is illegal in Java:

  public BigFraction(BigDecimal d)
  {
    BigInteger numerator = null;
    BigInteger denominator = null;
    if(d.scale() < 0)
    {
      numerator = d.unscaledValue().multiply(BigInteger.TEN.pow(-d.scale()));
      denominator = BigInteger.ONE;
    }
    else
    {
      numerator = d.unscaledValue();
      denominator = BigInteger.TEN.pow(d.scale());
    }
    this(numerator, denominator);
  }






更新

有很好的答案,但到目前为止,还没有提供我完全满意的答案,但我不在乎开始一个赏金,所以我正在回答我自己的问题(主要是为了摆脱那个令人讨厌的你考虑过标记一个接受的答案的消息)。

There have been good answers, but thus far, no answers have been provided that I'm completely satisfied with, but I don't care enough to start a bounty, so I'm answering my own question (mainly to get rid of that annoying "have you considered marking an accepted answer" message).

已经做过的变通办法建议是:

Workarounds that have been suggested are:


  1. 静态工厂。


    • 我在很多地方都使用过这个类,所以如果我突然摆脱了公共构造函数并且使用了valueOf(),代码就会破坏函数。

    • 这感觉就像是一个限制的解决方法。我不会获得工厂的任何其他好处,因为这不能被子类化,因为普通值没有被缓存/实习。


  • 这会导致大量代码膨胀。

  • 代码变得丑陋,因为在某些情况下我真的需要计算同时分子和分母,除非我返回 BigInteger [] 或某种私人内部类,否则我不能返回多个值。

  • This leads to lots of code bloat.
  • The code gets ugly because in some cases I really need to compute both numerator and denominator at the same time, and I can't return multiple values unless I return a BigInteger[] or some kind of private inner class.

针对此功能的主要论据是编译器必须检查您是否使用了调用超级构造函数之前的任何实例变量或方法,因为该对象将处于无效状态。我同意,但我认为这将是一个比确保所有最终实例变量始终在每个构造函数中初始化的更容易的检查,无论通过代码采用什么路径。另一个参数是你不能事先执行代码,但这显然是错误的,因为计算超级构造函数的参数是在某处执行的,所以必须允许它在字节码级别。

The main argument against this functionality is that the compiler would have to check that you didn't use any instance variables or methods before calling the superconstructor, because the object would be in an invalid state. I agree, but I think this would be an easier check than the one which makes sure all final instance variables are always initialized in every constructor, no matter what path through the code is taken. The other argument is that you simply can't execute code beforehand, but this is clearly false because the code to compute the parameters to the superconstructor is getting executed somewhere, so it must be allowed at a bytecode level.

现在,我想看到的是编译器无法让我接受此代码的一些原因:

Now, what I'd like to see, is some good reason why the compiler couldn't let me take this code:

public MyClass(String s) {
  this(Integer.parseInt(s));
}
public MyClass(int i) {
  this.i = i;
}

并像这样重写它(字节码基本相同,我会认为):

And rewrite it like this (the bytecode would be basically identical, I'd think):

public MyClass(String s) {
  int tmp = Integer.parseInt(s);
  this(tmp);
}
public MyClass(int i) {
  this.i = i;
}

我在这两个例子中看到的唯一真正区别是 tmp 变量的范围允许在第二个示例中调用 this(tmp)后访问它。因此,可能需要引入特殊语法(类似于 static {} 类初始化的块):

The only real difference I see between those two examples is that the "tmp" variable's scope allows it to be accessed after calling this(tmp) in the second example. So maybe a special syntax (similar to static{} blocks for class initialization) would need to be introduced:

public MyClass(String s) {
  //"init{}" is a hypothetical syntax where there is no access to instance
  //variables/methods, and which must end with a call to another constructor
  //(using either "this(...)" or "super(...)")
  init {
    int tmp = Integer.parseInt(s);
    this(tmp);
  }
}
public MyClass(int i) {
  this.i = i;
}


推荐答案


我觉得这很难看,但它有助于
我避免重复代码。
以下是我想要做的,但是
它在Java中是非法的...

I find this pretty ugly, but it helps me avoid duplicating code. The following is what I'd like to do, but it is illegal in Java ...

您还可以通过使用返回新对象的静态工厂方法来解决此限制:

You could also work around this limitation by using a static factory method that returns a new object:

public static BigFraction valueOf(BigDecimal d)
{
    // computate numerator and denominator from d

    return new BigFraction(numerator, denominator);
}

或者,您可以通过调用私有静态方法来进行计算来作弊你的构造函数:

Alternatively, you could cheat by calling a private static method to do the computations for your constructor:

public BigFraction(BigDecimal d)
{
    this(computeNumerator(d), computeDenominator(d));
}

private static BigInteger computeNumerator(BigDecimal d) { ... }
private static BigInteger computeDenominator(BigDecimal d) { ... }        

这篇关于为什么必须首先在Java构造函数中委托给不同的构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆