Java错误:默认构造函数的隐式超类构造函数未定义

99

我有一些简单的Java代码,其结构类似于以下代码:

abstract public class BaseClass {
    String someString;
    public BaseClass(String someString) {
        this.someString = someString;
    }
    abstract public String getName();
}

public class ACSubClass extends BaseClass {
    public ASubClass(String someString) {
        super(someString);
    }
    public String getName() {
        return "name value for ASubClass";
    }
}

我将会有很多个BaseClass的子类,每个子类以自己的方式实现getName()方法(模板方法模式)。

这个方案很好,但我不喜欢在子类中添加冗余的构造函数。这样做更加繁琐且难以维护。如果我要改变BaseClass构造函数的方法签名,我就需要改变所有子类的构造函数。

当我从子类中移除构造函数后,我得到了这个编译时错误:

Implicit super constructor BaseClass() is undefined for default constructor. Must define an explicit constructor

我想做的事情是否可能实现?


1
请不要删除“冗余”的构造函数!它可以保持代码的可读性,而且所有现代IDE都可以自动创建它,所以你只需要按一下快捷键即可。 - Andreas Dolk
3
重新阅读自己一年前的问题,我意识到我可以像Matt B建议的那样删除构造函数(包括基类中的构造函数),然后使用静态工厂方法来构建实例。 - Joel
12个回答

150
您之所以会出现此错误,是因为一个没有构造函数的类有一个默认构造函数,该构造函数是无参数的,并且等效于以下代码:

您遇到这个错误是因为一个没有构造函数的类有一个默认构造函数,它是无参数的并且等同于以下代码:

public ACSubClass() {
    super();
}

然而,由于你的 BaseClass 声明了一个构造函数(因此没有默认的无参构造函数,编译器也不会提供),这是非法的-继承 BaseClass 的类不能调用 super(); ,因为在 BaseClass 中没有无参数的构造函数。

这可能有点违反直觉,因为你可能认为子类自动拥有基类的所有构造函数。

最简单的解决方法是让基类不声明构造函数(因此具有默认的无参构造函数),或者声明一个无参构造函数(可以是自己或与其他构造函数一起)。但通常无法应用这种方法,因为你需要传递到构造函数中的任何参数来构造类的合法实例。


20
这可能有点出人意料,因为你可能认为子类会自动继承基类所有的构造函数。 - Mr_and_Mrs_D
2
为了后人的利益,我建议未来的读者采用我的解决方案:在BaseClass中创建一个无参构造函数,但是让它简单地抛出UnsupportedOperationException或其他异常。这不是最好的解决方案(它错误地暗示该类可以支持无参构造函数),但这是我能想到的最好的解决方案。 - JMTyler

53
对于那些在谷歌搜索此错误并来到这里的人,可能会有另一个原因导致出现此错误。当您的项目设置 - 系统配置不匹配时,Eclipse会出现此错误。
例如,如果您将Java 1.7项目导入Eclipse,并且您没有正确设置1.7,则会出现此错误。然后您可以前往项目 - 首选项 - Java - 编译器,并切换到1.6或更早版本;或者前往窗口 - 首选项 - Java - 已安装的JRE,并添加/修复您的JRE 1.7安装。

2
在Eclipse中,我突然出现了这个错误,但是没有明显的原因。然后我清理了工作区(菜单项目->清理...),问题就解决了。 - erickrf
安装的Java版本(例如Java 11)和Eclipse中的兼容级别(我认为是1.6)之间有什么关系? - Kennerdol
我的程序在Ubuntu 20.04上运行得很好,但升级到22.04后出现了错误,我仍在努力解决。Dean先生,您说的导入Java是什么意思? - Kennerdol
我的系统上安装的是“openjdk version "11.0.16" 2022-07-19 OpenJDK Runtime Environment (build 11.0.16+8-post-Ubuntu-0ubuntu122.04) OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)”。 - Kennerdol

8

这是可能的,但不是你所想的那种方式。

你需要在基类中添加一个无参构造函数,就可以了!

public abstract class A {
    private String name;
    public A(){
        this.name = getName();
    }
    public abstract String getName();


    public String toString(){
        return "simple class name: " + this.getClass().getSimpleName() + " name:\"" + this.name + "\"";
    }
}
class B extends A {
    public String getName(){
        return "my name is B";
    }
    public static void main( String [] args ) {
        System.out.println( new C() );
    }
}
class C extends A {
    public String getName() {
        return "Zee";
    }
}

当您不向类添加构造函数时,编译器会为您添加默认的无参构造函数。
当默认的无参构造函数调用super()时,由于父类中没有它,因此您会收到该错误消息。
这就是问题本身。现在,扩展答案:
您是否知道创建子类(行为)以指定不同值(数据)是毫无意义的?!希望您知道。
如果唯一变化的是“名称”,那么一个参数化的单个类就足够了!
所以您不需要这个:
MyClass a = new A("A");
MyClass b = new B("B");
MyClass c = new C("C");
MyClass d = new D("D");

或者
MyClass a = new A(); // internally setting "A" "B", "C" etc.
MyClass b = new B();
MyClass c = new C();
MyClass d = new D();

当你能够写出以下代码时:
MyClass a = new MyClass("A");
MyClass b = new MyClass("B");
MyClass c = new MyClass("C");
MyClass d = new MyClass("D");

如果我更改BaseClass构造函数的方法签名,我必须更改所有子类。

这就是为什么继承是创建高耦合的工具,这在面向对象系统中是不可取的。应该避免使用它,并可能用组合代替。

请考虑是否真的需要它们作为子类。这就是为什么经常使用接口的原因:

 public interface NameAware {
     public String getName();
 }



 class A implements NameAware ...
 class B implements NameAware ...
 class C ... etc. 

在这里,B和C本来可以从A继承,这会在它们之间创建非常高的耦合度,但是通过使用接口可以减少耦合度,如果A决定不再是"NameAware",其他类也不会受到影响。

当然,如果你想要重用行为,这种方法就不可行了。


2
是的,除非您不能再确保实例已正确初始化(例如,在这种特定情况下具有名称)。 - ChssPly76
@ChssPly76:是的,但这可能是因为继承被使用得不好。我已经扩展了我的答案来涵盖它。 - OscarRyz

4
您可能会在未设置JRE时遇到此错误。如果是这样,请尝试将 JRE系统库 添加到您的项目中。
在Eclipse IDE下:
  1. 打开菜单项目--> 属性,或右键单击包资源管理器中的项目,然后选择属性(Windows上的Alt+Enter,Mac上的Command+I)
  2. 单击Java Build Path,然后单击Libraries选项卡
  3. 选择ModulepathClasspath,然后按添加库...按钮
  4. 选择JRE系统库,然后单击下一步
  5. 保持选中工作区默认JRE(您也可以选择其他选项),然后单击完成
  6. 最后,按应用和关闭

2

另一种方法是在派生类构造函数的第一条语句中使用所需的参数调用super()。

public class Sup {
    public Sup(String s) { ...}
}

public class Sub extends Sup {
    public Sub() { super("hello"); .. }
}

1
我已经解决了上述问题,具体步骤如下:
  1. 点击项目。
  2. 点击属性 > Java Build Path > Library > JRE System Library > 编辑
  3. 选择默认的系统JRE并完成
  4. 应用并关闭。

抓住了!完美! - Ronald Calazans

1

这里是一个简单的例子

public class A {
    public A(int i){
       //
    }

 }

class B extends A {
    public B(int i){
      super(i);
    }
  }

0
简短回答: 在基类/父类/超类中添加一个没有参数的构造函数。例如,
        class Parent{
    
    String name;
    int age;
    String occupation;
            //empty constructor
            public Parent(){
            
        }
    
    public Parent(String name, int age, String employment){
    this.name = name;
    this.age = age;
    this.occupation = employment;
    }
}

// 在父类中,您可以根据需要添加任何其他构造函数,也就是构造函数重载。

如果超类没有无参构造函数,则会出现编译时错误。Object具有这样的构造函数,因此如果Object是唯一的超类,则没有问题。

// 然后让我们继承

    class Child extends Parent{
              Child(String name, int age){
this.name = name;
this.age = age;
    }
    
    }

使用super(),将调用超类的无参构造函数;而使用super(parameter list),将调用超类与参数列表匹配的构造函数。


0
如果在子类构造函数中没有将调用父类构造函数作为第一条语句,Eclipse 将会报错。

0

很抱歉打扰了,但我今天遇到了这个问题。对于所有也面临这个问题的人 - 可能的原因之一是您没有在方法的第一行调用super。 第二,第三和其他行会触发此错误。 在您的方法中,调用super应该是最先的调用。 在这种情况下,一切都很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接