I have two abstract classes one inheritance from the other. In The Child abstract class I don’t have any code inside besides the declaration of the class. And in the parent abstract class I have a private int quality and a constructor that receives an int. For some reason without any code the compiler give me compile time error. As it tries to invoke parent class constructor but java gave his child only the default one.
My question is: Why with out any code the compiler gives me that error.
CodePudding user response:
from what you write this seems to be the situation:
// 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 {
}
The compiler will compile these classes because when it generates .class files the classes it generates have to be functional. You say 'without any code' but the above is code. It is just not code that you are using anywhere.
However the .class files that javac generates could be linked to from another source. The compiler doesn't know so it makes sure that the code compiles or it generates an error.
in your case if you define a constructor, the parent constructor must be called explicitely or the default constructor is assumed.
so your class B defines no constructor. A default constructor is generated invisibly. This default constructor can only try to call it's super constructor. Specifically it calls the parent default constructor. Except that constructor isn't there.
The generated constructor cannot call the constructor with 1 argument because it cannot know what to put in for that argument. So basically if you have a constructor argument, your child classes need to either provide that argument or you on the parent level provide a default constructor.
so both of these work:
public abstract class A {
private int quality;
public A(int quality) {
this.quality = quality;
}
}
public abstract class B extends A {
public B() {
super(0);
}
}
or
public abstract class A {
private int quality;
public A() {
this(0);
}
public A(int quality) {
this.quality = quality;
}
}
public abstract class B extends A {
}
In the first case the child class explicitly calls the parent constructor. In the second the parent takes responsibility for knowing what to do with the value.
Probably in your case (if you expect another class to extend B and provide implementation) you would want to do this though:
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);
}
}
Then when you extends this class with a non-abstract (concrete) class it can provide the value up the chain.
And finally once more because I feel this is the idea behind your question:
Just because the code isn't being utilized in the rest of your project doesn't mean the compiler isn't trying to create a usable class. because you can grab any class file from your project and copy it elsewhere and use it. So what the compiler delivers has to be correct or it throws an error.
CodePudding user response:
You have these two class definitions (or something equivalent).
abstract class Parent {
private int quality;
Parent(int quality) {
this.quality = quality;
}
}
abstract class Child extends Parent {
}
Let's first look at Parent
's constructor(s). There is only one, and it requires the caller to provide a quality
value. You explicitly don't provide a constructor that allows you to leave the quality
unspecified.
Now you have a Child
class that extends Parent
. Do don't specify any constructor, so Java's default applies, to silently create a dumb no-args constructor.
The Child
class extends its Parent
class, meaning that every Child
instance has all the properties of the Parent
, including the quality
. But when constructing a Child
, you didn't provide a way to specify what value to use for quality
.
In the Java inheritance system, a constructor must always start by initializing the parent class aspects of the instance, by calling a parent constructor, using the super(...)
syntax. If you don't write that call yourself, it's silently inserted into your constructor.
It's good practice and often necessary to write explicit constructors instead of relying on some dumb automatics. And in case of inheritance, a constructor should begin with a super(...)
call, specifying how to initialize the parent aspects of the instance to be created, in your case supplying a quality
value to the parent constructor.
So, depending on your requirements, a solution might be
abstract class Child extends Parent {
Child() {
super(42); // All Child instances get a constant quality=42
}
}
or
abstract class Child extends Parent {
// When cinstructing a Child instance,
// an explicit quality value must be supplied.
Child(int quality) {
super(quality);
}
}
After applying all built-in Java automatic code rules, your original version is equivalent to
// 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.
}
}
CodePudding user response:
Add the constructor to the child class and use super()
inside the constructor.
example:
abstract class Parent{
private int x;
Parent(int x){
this.x = x;
}
}
abstract class Child extends Parent{
Child(int y){
super(y);
}
}
Now no compile errors :)