【问题标题】:Out of order chained constructors乱序链式构造函数
【发布时间】:2011-11-22 11:34:39
【问题描述】:
public class ParentClass
{
    public ParentClass(int param);
}

public class MyClass extends ParentClass
{
    private int _a;
    private int _b;
    private int _c;

    public MyClass(String input)
    {
        _a=CalculateA(input);
        _b=CalculateB(_a);
        _c=CalculateC(_a);
        super(_b+_c);
    }

    //a expensive procedure
    private int CalculateA(String text);

    private int CalculateB(int a);
    private int CalculateC(int a);  
}

Java 不允许链式构造函数是放在构造函数中的第一个方法以外的任何东西。 链式构造函数不能将非静态方法作为参数调用(这消除了使用返回初始化值的 Initialsers 的可能性)。

如何使用合法的Java实现上述代码?

【问题讨论】:

  • 有什么问题?并且构造函数绝对可以调用非静态方法。
  • "构造函数也不能调用非静态方法。"那对我来说是个新闻。我很确定我在构造函数中使用了大量方法。
  • 那么问题是什么?目前你已经发表了声明。
  • param 是父类私有的吗?它必须是吗?如果没有,您可以调用super(-1);(或另一个虚拟值),在子类构造函数中进行计算,然后分配param = _b+_c;
  • param 实际上是构造函数的参数,它根本不存储在父类中。 (除了在构造函数中的本地)

标签: java inheritance constructor


【解决方案1】:

编辑 事实上,Java 不允许构造函数在调用父类构造函数之前进行任何计算,即使这些仅涉及静态方法(正如您的 calculateX 应该那样)和结果仅分配给类私有的变量(例如您的_a_b_c)或构造函数的本地变量。

但是有一种解决方法:调用另一个构造函数,并将 calculateX 调用的结果分配给其参数 - 然后您可以在整个其他构造函数中访问此结果。

public class MyClass extends ParentClass {
    private int _a,_b,_c;

    public MyClass(String input) {
        this(calculateA(input));
    }

    private MyClass(int a) {
        this(a, calculateB(a), calculateC(a));
    }

    private MyClass(int a, int b, int c) {
        super(b + c);
        this._a = a;
        this._b = b;
        this._c = c;
    }

    private static int calculateA(String text) {
        try {Thread.sleep(1000);} catch (Exception e) {}  // expensive ;-)
        return text.length();
    }

    private static int calculateB(int a) { /* ... */ }
    private static int calculateC(int a) { /* ... */ }
}

编辑 2 由于需要存储更多计算或更多中间结果以供以后使用,这种方法将导致构造函数链更长,仅包含 this(...)-调用。一个更奇特的解决方案,只有两个构造函数,一个是公共的,一个是私有的,可以使用辅助类(合理地是一个内部类):

    public MyClass(String input) {
        this(new InitCalcResult(input));
    }

    private MyClass(InitCalcResult initCalcResult) {
        super(initCalcResult.initB + initCalcResult.initC);
        this._a = initCalcResult.initA;
        this._b = initCalcResult.initB;
        this._c = initCalcResult.initC;
    }

    private static class InitCalcResult {
        private int initA, initB, initC;

        InitCalcResult(String input) {
            initA = calculateA(input);
            initB = calculateB(initA);
            initC = calculateC(initA);  
        }
    }

(使用与上述相同的私有字段和静态calculateX 方法)。

【讨论】:

  • 这是我的原创剧本。问题是我仍然需要 _a。
  • @Oxinabox _a 的值在我的super 调用行中计算,然后通过calculateBPlusC 传递给calulateB 和calculateC。你的意思是你还需要 _a 供以后使用吗?
  • 确实,我需要_a,_b和_c以备后用,
  • 我曾考虑过静态工厂方法。问题是这个特定库中的所有其他类都是用那里的构造函数实例化的。我的主管会一直保持这种状态(并且有充分的理由)
  • 我认为确实有一种方法可以得到你想要的。我已经替换了我以前的答案,因为我认为新方法要好得多(但感谢您接受答案,即使它没有完全回答您的问题;-))-
【解决方案2】:

你可以做这样的事情。

public abstract class ParentClass
{
    public ParentClass(String input){
        int a = getData(input);
        /* Do what ever u need to do with a*/
    };

    public abstract int getData(String input);
}


public class MyClass extends ParentClass
{
    private int _a;
    private int _b;
    private int _c;

    public MyClass(String input)
    {
        super(input);
    }

    public int getData(String input){
         _a=CalculateA(input);
         _b=CalculateB(_a);
         _c=CalculateC(_a);
         return _b+_c;
    }

    //a expensive procedure
    private int CalculateA(String text){/* return int */};

    private int CalculateB(int a){/* return int */};
    private int CalculateC(int a){/* return int */};  
}

由于 getData 是抽象的,因此将调用基类函数。超类将获得所需的数据。

【讨论】:

  • 这可以工作,不是抽象的,因为 Parent 不是抽象类。但是虚拟(等等,虚拟是java中的默认值,对吧?)。虽然凌乱,但 Parent 是其他 2 个类的基类,并且本身就是一个功能类。此外,Parent 的构造函数实际上链接到“GrandParent”的构造函数。它变得更加混乱,因为虽然我在示例中只为 Paret 构造函数输入了 1 个参数,但实际上是 5 个。
  • 不要这样做。如果有人使用在子类的构造函数中初始化的字段来实现抽象方法,则在调用该方法时该字段仍然是null。这可能会导致一些不明显的错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
  • 2018-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多