繁体   English   中英

Java 构造函数继承

[英]Java Constructor Inheritance

我想知道为什么在java构造函数中没有继承? 你知道当你有这样的课程时:

public class Super {

  public Super(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    this.serviceA = serviceA;
    //etc
  } 

}

稍后当您从Super继承时,java 会抱怨没有定义默认构造函数。 解决方案显然是这样的:

public class Son extends Super{

  public Son(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
    super(serviceA,serviceB,serviceC);
  }

}

这段代码是重复的,不是 DRY 和无用的(恕我直言)......所以又带来了问题:

为什么java不支持构造函数继承? 不允许这种继承有什么好处吗?

假设构造继承...然后因为每个类都最终从对象派生,每个班级将最终有一个参数的构造函数。 这是个坏主意。 你究竟会期待什么:

FileInputStream stream = new FileInputStream();

去做?

现在可能应该有一种方法可以轻松创建相当常见的“传递”构造函数,但我认为它不应该是默认值。 构造子类所需的参数通常与超类所需的参数不同。

当您从 Super 继承时,实际上会发生以下情况:

public class Son extends Super{

  // If you dont declare a constructor of any type, adefault one will appear.
  public Son(){
    // If you dont call any other constructor in the first line a call to super() will be placed instead.
    super();
  }

}

所以,这就是原因,因为您必须调用唯一的构造函数,因为“Super”没有默认构造函数。

现在,试图猜测为什么 Java 不支持构造函数继承,可能是因为构造函数只有在谈论具体实例时才有意义,并且当你不知道它是如何定义的时候,你不应该能够创建它的实例(通过多态性)。

因为构造子类对象的方式可能与构造超类的方式不同。 您可能不希望子类的客户端能够调用超类中可用的某些构造函数。

一个愚蠢的例子:

class Super {
    protected final Number value;
    public Super(Number value){
        this.value = value;
    }
}

class Sub {
    public Sub(){ super(Integer.valueOf(0)); }
    void doSomeStuff(){
        // We know this.value is an Integer, so it's safe to cast.
        doSomethingWithAnInteger((Integer)this.value);
    }
}

// Client code:
Sub s = new Sub(Long.valueOf(666L)): // Devilish invocation of Super constructor!
s.doSomeStuff(); // throws ClassCastException

或者更简单:

class Super {
    private final String msg;
    Super(String msg){
        if (msg == null) throw new NullPointerException();
        this.msg = msg;
    }
}
class Sub {
    private final String detail;
    Sub(String msg, String detail){
        super(msg);
        if (detail == null) throw new NullPointerException();
        this.detail = detail;
    }
    void print(){
        // detail is never null, so this method won't fail
        System.out.println(detail.concat(": ").concat(msg));
    }
}
// Client code:
Sub s = new Sub("message"); // Calling Super constructor - detail is never initialized!
s.print(); // throws NullPointerException

从这个例子中,你看到你需要某种方式来声明“我想继承这些构造函数”或“我想继承除这些构造函数之外的所有构造函数”,然后你还必须指定一个默认的构造函数继承以防万一有人在超类中添加了一个新的构造函数......或者如果你想“继承”它们,你可以只要求你重复超类的构造函数,这可以说是更明显的方法。

因为构造函数是一个实现细节——它们根本不是接口/超类的用户可以实际调用的东西。 当他们得到一个实例时,它已经被构建了; 反之亦然,在您构造一个对象时,根据定义,它当前没有分配给任何变量。

想想强制所有子类都有一个继承的构造函数意味着什么。 我认为直接传递变量比让类“神奇地”拥有一个带有一定数量参数的构造函数更清晰,仅仅因为它是父类。

大卫的回答是正确的。 我想补充一点,你可能会从上帝那里得到一个信号,表明你的设计一团糟,“儿子”不应该是“超级”的子类,相反,超级有一些实现细节最好地表达通过拥有Son 提供的功能,作为一种策略。

编辑:Jon Skeet 的回答是最棒的。

构造函数不是多态的。
在处理已经构造的类时,您可能正在处理对象的声明类型或其任何子类。 这就是继承的用处。
构造函数总是在特定类型上调用,例如new String() 假设的子类在这方面没有作用。

因为(超)类必须完全控制它的构造方式。 如果程序员认为提供默认(无参数)构造函数作为类契约的一部分是没有意义的,那么编译器就不应该提供。

您本质上确实继承了构造函数,因为您可以在适当的时候简单地调用 super,只是由于其他人提到的默认情况下发生的原因,它很容易出错。 编译器无法推测什么时候合适,什么时候不合适。

编译器的工作是提供尽可能多的灵活性,同时降低复杂性和意外副作用的风险。

我不知道子类继承构造函数的任何语言(但是,我不是一个编程多语言者)。

这是关于 C# 的相同问题的讨论。 普遍的共识似乎是它会使语言复杂化,给基类中的更改带来潜在的不良副作用,并且在良好的设计中通常不需要。

派生类与其基类不是同一个类,您可能关心也可能不关心基类的任何成员在构造派生类时是否被初始化。 这是由程序员而非编译器做出的决定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM