【问题标题】:Abstract class java inheritance [duplicate]抽象类java继承[重复]
【发布时间】:2022-02-18 18:43:14
【问题描述】:

我有两个抽象类,一个继承自另一个。在 Child 抽象类中,除了类的声明之外,我没有任何代码。在父抽象类中,我有一个私有的 int 质量和一个接收 int 的构造函数。 由于某种原因,没有任何代码,编译器会给我编译时错误。 因为它试图调用父类构造函数,但 java 只给了他的孩子默认的。

我的问题是: 为什么没有任何代码编译器会给我这个错误。

【问题讨论】:

    标签: java inheritance abstract-class


    【解决方案1】:

    从你写的情况来看,情况似乎是这样的:

    // in a file A.java
    public abstract class A {
        private int quality;
        public A(int quality) {
            this.quality = quality;
        }
    }
    
    // in another file B.java
    public abstract class B extends A {
        
    }
    

    编译器将编译这些类,因为当它生成 .class 文件时,它生成的类必须是功能性的。您说“没有任何代码”,但上面是代码。这不是您在任何地方使用的代码。

    但是,javac 生成的 .class 文件可以从另一个源链接到。编译器不知道,所以它确保代码编译或生成错误。

    在您的情况下,如果您定义了构造函数,则必须显式调用父构造函数或假定默认构造函数。

    所以你的 B 类没有定义构造函数。默认构造函数是不可见的。这个默认构造函数只能尝试调用它的超级构造函数。具体来说,它调用父级默认构造函数。除了构造函数不存在。

    生成的构造函数不能调用带有 1 个参数的构造函数,因为它不知道为那个参数输入什么。所以基本上如果你有一个构造函数参数,你的子类需要要么提供那个参数,要么你在父级提供一个默认构造函数。

    所以这两个都有效:

     public abstract class A {
        private int quality;
        public A(int quality) {
            this.quality = quality;
        }
    }
    
    public abstract class B extends A {
        public B() {
            super(0);
        }
    }
    

    public abstract class A {
        private int quality;
        public A() {
            this(0);
        }
        public A(int quality) {
            this.quality = quality;
        }
    }
    
    public abstract class B extends A {
        
    }
    

    在第一种情况下,子类显式调用父构造函数。在第二个中,父母负责知道如何处理该值。

    可能在您的情况下(如果您希望另一个类扩展 B 并提供实现)您可能希望这样做:

    public abstract class A {
        private int quality;
        public A(int quality) {
            this.quality = quality;
        }
    }
    
    public abstract class B extends A {
        public B(int quality) {
            super(quality);
        }
    }
    

    然后,当您使用非抽象(具体)类扩展此类时,它可以提供价值链。

    最后再一次,因为我觉得这是您问题背后的想法:

    仅仅因为代码没有在项目的其余部分中使用并不意味着编译器没有尝试创建可用的类。因为您可以从项目中获取任何类文件并将其复制到其他地方并使用它。所以编译器传递的内容必须是正确的,否则会引发错误。

    【讨论】:

    • 仅仅因为代码没有在项目的其余部分中使用并不意味着编译器没有尝试创建可用的类。因为您可以从项目中获取任何类文件并将其复制到其他地方并使用它。所以编译器提供的内容必须是正确的,否则会引发错误。谢谢*
    【解决方案2】:

    你有这两个类定义(或等效的东西)。

    abstract class Parent {
        private int quality;
        Parent(int quality) {
            this.quality = quality;
        }
    }
    
    abstract class Child extends Parent {
        
    }
    

    让我们先看看Parent 的构造函数。只有一个,它要求调用者提供一个quality 值。您明确不提供允许您不指定 quality 的构造函数。

    现在你有一个扩展ParentChild 类。不要指定任何构造函数,因此应用 Java 的默认值,以静默地创建一个愚蠢的无参数构造函数。

    Child 类扩展了它的Parent 类,这意味着每个Child 实例都具有Parent 的所有属性,包括quality。但是在构造Child 时,您没有提供一种方法来指定要为quality 使用什么值。

    在 Java 继承系统中,构造函数必须始终从初始化实例的父类方面开始,通过调用父构造函数,使用 super(...) 语法。如果您不自己编写该调用,它会以静默方式插入到您的构造函数中。

    编写显式构造函数而不是依赖一些愚蠢的自动构造函数是一种很好的做法,而且通常是必要的。在继承的情况下,构造函数应该以 super(...) 调用开始,指定如何初始化要创建的实例的父方面,在您的情况下,向父构造函数提供 quality 值。

    因此,根据您的要求,解决方案可能是

    abstract class Child extends Parent {
        Child() {
            super(42); // All Child instances get a constant quality=42
        }
    }
    

    abstract class Child extends Parent {
        // When cinstructing a Child instance, 
        // an explicit quality value must be supplied.
        Child(int quality) {
            super(quality);
        }
    }
    

    应用所有内置的Java自动代​​码规则后,您的原始版本相当于

    // Auto: If you don't specify a parent class, it's java.lang.Object
    abstract class Parent extends Object {
        private int quality;
        Parent(int quality) {
            super(); // Auto: calls the `Object()` constructor to initialize
                     // aspects that every Java instance needs
            this.quality = quality;
        }
    }
    
    abstract class Child extends Parent {
        // Auto: no constructor supplied in source, so a dumb one gets generated 
        Child() {
            super(); // Auto: calls the parent's no-args constructor
                     // But there is no such constructor, hence compile error.
        }
    }
    

    【讨论】:

    • 我的问题是为什么编译器给我一个编译时错误,而我没有尝试创建一个子类型的对象。这意味着我没有做任何事情来证明初始化父类方面的合理性。
    • 您可以将编译器错误消息理解为标记您创建了一个永远无法使用的类Child。要么继续让它可用,要么删除它。
    【解决方案3】:

    将构造函数添加到子类并在构造函数中使用super()

    示例:

    abstract class Parent{
        private int x;
        Parent(int x){
            this.x = x;
        }
        
    }
    
    abstract class Child extends Parent{
        Child(int y){
            super(y);
        }
        
    }
    

    现在没有编译错误:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      • 2021-01-02
      • 2016-11-05
      • 1970-01-01
      • 2013-08-04
      • 2011-03-15
      相关资源
      最近更新 更多