class A{
A(int x){}
}
class B extends A{
B(int x){}
public static void main(String args[]){
B b = new B(10);
}
}
我理解这会抛出一个错误(B的一个arg构造函数,隐式调用super(),因此A)不存在default-arg构造函数。我很好奇为什么B的一个arg构造函数,不使用super(x)来调用类A的一个arg构造函数。相反,让我陷入为A明确写一个无参数构造函数的麻烦,当我不需要一个!
答案 0 :(得分:11)
您可以像这样重写B类:
class B extends A{
B(int x) {
super(x);
}
}
这将做你想要的。它不是隐式发生的,因为A中可能有很多不同的构造函数(在你的例子中没有,但是可以)并且它不知道哪一个是正确的。如果你有一个长度和宽度的类,由于某种原因覆盖了一个带宽度和高度的类,该怎么办?您不希望Java仅仅因为参数的类型匹配而自动调用超类构造函数。您可能需要编译错误,强制您使用适当/正确的参数显式调用超类构造函数。
答案 1 :(得分:4)
你得到一个隐式的零参数构造函数调用super的原因是编译器确实知道你想要超构造 - 否则就没有意义 - 而零参数构造函数是一个简单,自然的,并且通常是正确的选择要求编译器比它更聪明,这是不现实的。你可以说,“嘿,只有一个构造函数,它需要相同数量的参数 - 它有多难?”但这真的很难;在这种情况下你可能想要的东西是敞开的。所以安全策略是:如果你想要的不是no-arg构造函数,编译器要求你明确地调用它。
答案 2 :(得分:2)
您需要做的就是
B(int x){ super(x); }
不需要默认构造函数,但您需要从A调用one-arg构造函数。
似乎编译器可以帮助你,但Java有时会像这样冗长。
答案 3 :(得分:1)
一个无参数的构造函数,你总是知道它的作用,因为它不依赖于任何外部数据; OTOH是一个带有一些参数的构造函数,比较器不能确定它们的意思所以它要求你(人类)确认使用哪一个。
给出的例子:
class A{
A(int numberOfYears){}
}
class B extends A{
B(int numberOfApples){}
public static void main(String args[]){
B b = new B(10);
}
}
我想这更清楚地表明了编译器默认情况下不使用super(int)
构造函数的原因。
答案 4 :(得分:1)
正如 Python的禅> 告诉我们:Explicit is better than Implicit
所以将B(int x){}
更改为B(int x) { super(x); }
答案 5 :(得分:0)
然后,您应该在函数的开头显式调用super(x)。基本上Java会添加一个对super()的调用,如果它没有找到任何类型的超级调用 - 所以添加你自己的,你会没事的。
答案 6 :(得分:0)
可能是因为java设计者认为超类中存在默认构造函数的可能性比你想要调用的签名具有相同签名的构造函数更有可能。也许是因为规则“如果你没有显式调用super,它使用默认构造函数”比“如果你没有显式调用super,它使用具有相同签名的超类的构造函数”更简单。
答案 7 :(得分:0)
我很好奇为什么一个arg构造函数 对于B,不使用super(x)来调用 A类的一个arg构造函数。
如果你问这是一个语言设计问题......
我想Java设计者认为隐式super
“调用”的语义更复杂
会更难理解。例如,如果超类构造函数参数的类型与子类中的参数类型不同,该怎么办?如果超类型中有多个构造函数会怎么样?
我个人认为在每个构造函数中包含显式super
调用并不是一种负担。的确,很多人都认为这是好风格。
答案 8 :(得分:0)
原因是,当您在子类中定义参数化构造函数时,您希望从超类中覆盖某些东西。所以超类不被称为AUTOMATICALLY。然而,您可以调用super(param)(如果您只是添加到该功能)。但那应该是第一次打电话。
然而,DEFAULT构造函数的名称为DEFAULT。所以当创建一个对象时,它总是被调用(以JLS指定的顺序)。
答案 9 :(得分:0)
正如其他人所指出的那样,在你给出它的例子中,显式调用one-arg构造函数并不是什么大问题。
更令我困扰的是,当我有一个带有两个子类B和C的超类A时,B和C都需要带有一堆参数的相同的10个构造函数。在我看来,整个继承点是我应该能够将这些构造函数放在A中,而不必两次编写相同的代码,甚至两次写入10个pass-thru函数。
如果我可以在由其子类继承的类中编写普通函数,为什么我不能编写构造函数?
我想知道这是否是其中一个问题,如果我阅读编译器的代码,我会发现自己说,“啊,我知道,如果他们试图这样做,他们会遇到这个和那个问题,这将是一个丑陋的混乱。“或者也许他们在编写代码的那天只是大脑死了。我从来没有真正听到过令人满意的解释,说明为什么不能这样做。
我想到的一个想法:假设B扩展A. A有一个返回int的函数x()。 B继承了这个函数,所以B现在有一个返回int的函数x()。 A有一个构造函数A,它实际上返回了一个A类型的对象。但是B的构造函数必须返回一个B类型的对象,而不是A.所以它不是真正继承的,它必须更像“外推”而不是继承。所以这不是一回事。
仍然,如果我有一个带有构造函数A(int x,int y)的A类,并且B扩展了A,编译器可以自动生成一个构造函数B(int x,int y),它调用A ...好像应该可行。当然,也许这不是我想要做的。但是后来给我一个覆盖它的方法,比如明确声明一个不同的构造B(int x,int y)。在现实生活中,我几乎总是在子类中重现我的所有构造函数。