我怎样才能避免在子类中创建无用的传递构造函数只是为了将参数传递给"super()"?

DVK*_*DVK 10 java parameters constructor subclass parameter-passing

在Java中,据我所知,子类继承具有参数的构造函数.

例如

public class Parent {
    public Parent(int x) {
        DoSomethingWithX(x);
    }
}

public class Child extends Parent {
    // Compile fails with "Implicit super constructor Parent() is undefined 
    // for default constructor. Must define an explicit constructor
}
Run Code Online (Sandbox Code Playgroud)

修复它的唯一方法是在Child类中创建一个无用的传递构造函数:

public class Child extends Parent {
    public Child(int x) {
         super(x);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

如果我有一个复杂的子类层次结构,有6-10个子类,那么为每个子类添加一个带有参数的无意义的传递构造函数似乎是个坏主意!

  • 代码看起来很愚蠢(10个类中相同方法的10个副本)
  • 更糟糕的是,与任何代码重复一样,代码变得脆弱 - 任何更改(例如,添加1个参数)都需要在10个位置而不是1个位置进行.

题:

有没有办法避免大型类层次结构的这个问题?

注意:

我知道一个解决方案(有一个setter,必须与构造函数分开调用)的参数.但是这个解决方案有几个很大的缺点,因为它们是不可接受的.

T.J*_*der 4

大约 15 年前,我遇到了与您类似的问题,其层次结构非常广泛(但只是稍深)(是的,伙计们,这是有原因的)但由于它们都派生自我们定期需要添加更多信息的基础,所以很快就发现向基础构造函数添加参数是非常痛苦的。

默认情况下不继承构造函数是一件好事,但有时您继承它们。我有时想要一个可以添加到类中的编译时注释,以告诉编译器“为我未显式实现的任何超级构造函数自动生成构造函数”。但我们没有,而且如果 JSR 真的成功的话,也需要花费数年的时间才能完成......

由于没有那个神奇的注释,我们使用的解决方案正是 @psabbate在他/她的评论中提到的解决方案:

如果您的构造函数收到一个 Map 或一个包含您可能需要的每个参数的 CustomClass 类,该怎么办?这样,如果您需要更改参数和参数,您只能更改您感兴趣的类。

...但具有参数类的特定类型层次结构(不是Map)。

为了完整起见,举一个例子:

// The standard parameters needed
class StandardParams {
    private String thisArg;

    public StandardParams(String thisArg) {
        this.thisArg = thisArg;
    }

    public String getThisArg() {
        return this.thisArg;
    }
}

// The base class
class Base {
    public Base(StandardParams args) {
        System.out.println("Base: " + args.getThisArg());
    }
}

// A standard subclass
class Sub1 extends Base {
    public Sub1(StandardParams args) {
        super(args);
        System.out.println("Sub1 thisArg: " + args.getThisArg());
    }
}
Run Code Online (Sandbox Code Playgroud)

对我来说,这甚至不是“最不糟糕”。拥有表示层次结构所需的基本信息的类型是富有表现力的。

如果子类需要比标准参数(为我们提供的)更多的信息,您可以选择将第二个参数类类型用作第二个参数(但树中有两种类型的构造函数)或使用参数类层次结构中的继承;我们使用了后者:

// Extended parameters (naturally you make these names meaningful)
class ExtendedParams extends StandardParams {
    private String thatArg;

    public ExtendedParams(String thisArg, String thatArg) {
        super(thisArg);
        this.thatArg = thatArg;
    }

    public String getThatArg() {
        return this.thatArg;
    }
}

// A subclass requiring extended parameter information
class Sub2 extends Base {
    public Sub2(ExtendedParams args) {
        super(args);
        System.out.println("Sub2 thisArg: " + args.getThisArg());
        System.out.println("Sub2 thatArg: " + args.getThatArg());
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的例子 IIRC 中,我们只有三个参数类(树中特定分支的标准一个和两个子类),跨越层次结构中的 30 个主类。

最后一点:对于我们来说,在构造参数类时需要向参数类提供一组核心内容,这些内容不会发生变化,并且有大约八个选项具有合理的基本默认值,但您可以覆盖。为了避免参数类中构造函数的爆炸,我们最终对它们做了一些穷人的构建器模式(“穷人的”,因为我们使这些类成为它们自己的构建器而不是将其分离出来;这是没有必要的严格地说,更改的内容具有廉价的默认值,因此即使构建时实例也始终处于有效状态)。所以我们的构建看起来像这样:

Thingy t = new Thingy(
    new ThingyParams(basic, construction, info)
    .withAnswer(42)
    .withQuestion("Life, the Universe, and Everything")
);
Run Code Online (Sandbox Code Playgroud)