首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免在子类中创建无用的传递构造函数,只为了将参数传递给"super()"?

如何避免在子类中创建无用的传递构造函数,只为了将参数传递给"super()"?
EN

Stack Overflow用户
提问于 2016-09-27 19:38:22
回答 3查看 1.3K关注 0票数 11

在Java中,据我所知,子类不继承带有参数的构造函数。

例如。

代码语言:javascript
复制
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
}

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

代码语言:javascript
复制
public class Child extends Parent {
    public Child(int x) {
         super(x);
    }
}

问题:

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

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

问题:

对于大型类层次结构,是否有避免此问题的方法?

注意:

我知道有一个解决方案(有一个setter,它必须被称为独立于构造函数)用于参数。但是这个解决方案有几个很大的缺点,因为它们是不可接受的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-09-28 08:22:20

大约在15年前,我遇到了一个与你类似的问题,有一个非常广泛(但只是稍微深一点)的等级(是的,伙计们,这是有原因的)。但是,由于它们都是从我们定期需要添加更多信息的基派生出来的,因此很明显,向基构造函数添加参数是非常痛苦的。

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

没有那个神奇的注释,我们使用的解决方案就是@psabbate mentioned in his/her comment

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

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

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

代码语言:javascript
复制
// 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());
    }
}

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

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

代码语言:javascript
复制
// 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());
    }
}

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

最后要注意的是:对于我们来说,在构造参数类时,我们需要提供一组核心的东西,这些东西是不变的,还有一些类似的选项,它们具有合理的基本缺省值,但可以重写。为了避免参数类中的构造函数激增,我们在它们上做了一些穷人的构建器模式(“可怜的人”,因为我们让类成为它们自己的构建器,而不是把它分开;只是没有必要那么严格,更改的东西有廉价的缺省值,所以即使在构建的时候,实例也总是处于有效状态)。所以我们的建筑看起来是这样的:

代码语言:javascript
复制
Thingy t = new Thingy(
    new ThingyParams(basic, construction, info)
    .withAnswer(42)
    .withQuestion("Life, the Universe, and Everything")
);
票数 4
EN

Stack Overflow用户

发布于 2016-09-28 14:55:26

使用工厂方法和反射:

代码语言:javascript
复制
public class A {
    protected A() {
        // No longer do initialization in constructor
        // initialize in init(), which is guaranteed to be called
    }

    protected A init(int i) {
        // do all initialization with i in here
        return this;
    };

    public static <T extends A> T create(Class<T> clazz, int i) {
        try {
            return (T) clazz.newInstance().init(i);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalArgumentException("not going to happen", e);
        }
    }
}

然后创建实例是:

代码语言:javascript
复制
B b = A.create(B.class, 86);

根据您想要的纪律级别,子类可以是简单的,如:

代码语言:javascript
复制
public class B extends A {
    // nothing special needed
}

但是允许直接实例化B b = new B()而不需要int,或者您可以收紧所有以防止出现这样的情况:

代码语言:javascript
复制
public class B extends A {
    protected B() {
        super();
    }
    // nothing else special needed
}

它需要通过工厂方法进行所有实例化。

在调用点更改代码可以很容易地使用IDE重构。

如果基类更改为需要更多/不同的初始化变量,则只需更改工厂方法(子类中没有任何更改),或者可以添加新的工厂方法,以保持旧方法的向后兼容性。

票数 4
EN

Stack Overflow用户

发布于 2016-09-27 20:14:48

Parent上只有一个带参数的构造函数这一事实告诉我,如果没有said int,就不能创建Parent。通过创建一个Child,您也通过继承创建了一个Parent。因为我们需要一个int来创建Parent,所以Child必须从某个地方提供它,无论是从它自己的构造函数参数还是从其他静态值。

如果不希望在Parent的每个子类中提供传递构造函数,则需要在其上提供一个no-arg构造函数,并定义这意味着什么。这可以是除了您已经拥有或替换它的构造函数之外,这取决于您。但是no构造函数对于你做你想做的事情来说是必要的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39732984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档