Subclasses inherting base class constructor while maintaining principle of code-reuse - java

So this code has an error because the base class has parameters right? Because the default constructor of each subclass calls the default constructor of the base class? and we don't have a default constructor for the base class which causes the error?
Did I misunderstand it, also what is the best way around this while implementing the principle of code-reuse because I'm trying to practice programming in OOP
class A {
int item;
A(int item) {
this.item = item;
}
}
class B extends A {
int subitem;
B(int item) {
super(item);
subitem = item * 2;
}
}
class C extends A {
}

Firstly, on the title of this question:
Subclasses inherting base class constructor while maintaining principle of code-reuse
Constructors are never inherited. It just doesn't happen. What you've been demonstrating is not inheritance - it's just the requirement that any constructor (other than the one in java.lang.Object) has to either chain to another constructor in the same class, or chain to a constructor in the superclass.
So this code has an error because the base class has parameters right?
Well, to be precisely, it's because the base class has no parameterless constructor.
Because the default constructor of each subclass calls the default constructor of the base class?
A default constructor is only provided if you don't specify any constructors explicitly. The default constructor always calls a parameterless constructor in the base class, implicitly.
and we don't have a default constructor for the base class which causes the error?
We don't have a parameterless constructor for the base class. Basically it's equivalent to writing:
class C extends A {
C() {
super();
}
}
If you understand why that doesn't compile, you understand why the version without any explicitly-declared constructors doesn't compile, as they're equivalent.
Did I misunderstand it, also what is the best way around this
Around what? You can't create an instance of A without providing an item. It would be bad if it did compile - what would the item be? Every instance of C can also be considered to be an A, which requires an item...
You could write a parameterless constructor which provides some sort of default to the superclass constructor:
public C() {
super("default item");
}
if that's what you want. But beyond that, we can't really suggest alternatives without knowing what you're trying to achieve.
Note that inheritance is far from the only way of achieving code reuse - and in fact I personally prefer reuse via composition instead of inheritance in general. Inheritance is powerful, but overused IMO.

your class C extends A by which it has to satisfy the contract of calling its one-arg constructor explicitly in your constructor, or you could simply have a no-args default constructor defined in your class A like:
class A {
int item;
A(int item) {
this.item = item;
}
A(){
//default stuff
}
}

Related

Possible to avoid default call to super() in Java?

Assume for some reason that I don't want to implicitly call super() which is done by default.
class Animal {
public Animal() {
System.out.println("Constructing an animal.");
}
}
class Dog extends Animal {
public Dog() {
System.out.println("Constructing a dog.");
}
public static void main(String[] a) {
new Dog();
}
}
Is there any way to "disable" the default behavior that super() is invoked when making a new Dog? Or would that be principally and conceptually wrong?
I mean there could be cases where you would want only the constructor of the subclass and not invoke the construction of the baseclass, and still inherit the baseclass.
If I understand you correctly you want to override the behavior of the Animal constructor or just not call it. If so you're conceptually wrong: you always need to call a super constructor, the only thing you could to with the calls is provide a non-default constructor, i.e. one with arguments and provide appropriate arguments to influence the behavior of that constructor (e.g. by either selecting a different constructor, passing some sort of strategy/function object etc.)
Another way might be to provide some init() method that the default constructor is calling and which you can override but there are a few problems with it, e.g.:
If the overridden method tries to access anything only visible to the subclass (e.g. additional fields) you could run into problems because those haven't been initialized yet.
That method can't initialize any final field.
s there any way to "disable" the default behavior that super() is
invoked when making a new Dog? Or would that be principally and
conceptually wrong?
It is conceptually wrong since any class which invokes its constructor has to invoke the constructor of its parent : super() or super() with any arguments if the super constructor has not it.
If the constructor of the super class does some specific processing and that is not desirable for all concrete classes, you should change rather the implementation of the super constructor and move the specific behavior in a method of the super class or in a method of concrete classes.
You can't bypass the construction of a parent class, and perhaps you shouldn't attempt to circumvent this rule.
But, if you must do this then you could build a no-op constructor in Animal (that you mark as protected so only derived classes can see it):
protected Animal(Noop foo)
{
/*do nothing explicit here*/
}
And call that one explicitly from your derived class using
super(new Noop())
Here I've invented a new class Noop (meaning "no operation") to enable the compiler to distinguish the constructor parameters.
Agreeing with all the answers here on invoking the constructor which cannot be avoided, providing one more workaround which ensures a block of code in constructor that gets executed only when it is directly invoked and not when any sub class constructors are invoked.
public class Animal{
public Animal() {
if(this.getClass() == Animal.class){
System.out.println("Animal");
}
}
}
public class Dog extends Animal{
public Dog() {
System.out.println("Dog");
}
}
That's one of the reasons inheritance is a pain and usually misused:
public interface Animal {
// define behaviour
}
public class Dog implements Animal {
// implement behaviour
}
Use interfaces and aggregation / decoration so you can avoid the struggle of bad design decisions in inheritance chains.

Can't call supertype constructor directly - why not?

I have the following example classes in Java:
public class A { }
public class Super {
protected Super() { }
public Super(A a) { }
}
public class Sub extends Super { }
public class Consumer {
public Consumer() {
Sub sub = new Sub(new A()); //compiler error
}
}
The compiler error states that the arguments cannot be applied to the default constructor in Sub, which is perfectly understandable.
What I'm curious about is the rationale behind this decision. Java generates the default empty constructor in Sub; why can't it call it behind the scenes in this case? Is this primarily a case of sane hand-holding, or is there a technical reason?
EDIT
I'm aware that this is a language limitation. I'm curious about why it is a language limitation.
EDIT 2
It seems that, as is often the case, I was too close to the code I was actually working in to see the big picture. I've posted a counter-example in the answers below that shows why this is a Bad Thing®.
I think it's an issue of both readibility and not assuming intent. You say
Java generates the default empty constructor; why can't it call it behind the scenes in this case?
Yet to me, it would make much more sense for Java to implicitly call the Super(A) constructor "behind the scenes" than to call the Super() constructor, disregarding A.
And there you have it. We already have two disparate assumptions about what should (or could) happen in this case.
One of the Java language's core principles is transparency. As much as possible, the programmer should be able to see by looking at the code what will happen, sometimes at the expense of convenience or magic at the syntax level.
A parallel tenet to that is not assuming intent: in cases where the programmer's intentions seem ambiguous, the Java language will sometimes favour a compile error rather than automatically chosing a default through some (arbitrary or otherwise) selection algorithm.
public class Sub extends Super { }
does not have the constructor Sub(A a), it only has the default constructor Sub().
Constructors are not inherited.
Base classes need to call super constructors in order to ensure an object is properly instantiated. For instance consider:
class Super {
final String field1;
public Super(String field1) {
this.field1 = field1;
}
...
}
class Base extends Super {
final String field2;
public Base(String field2) {
this.field2 = field2;
}
...
}
Does Base's constructor override the Super constructor? If so, then field1 is no longer guaranteed to be initialized, making inherited methods behave unexpectedly.
The moment you add a non-default constructor to the subclass then inherited constructors stop working. I think it'd be a confusing and rarely useful feature it was added to the language, although technically I can see no reason why it wouldn't be possible.
You've overidden the default public constructor, so there isn't anything to call.
So the class Sub is equivalent to
public class Sub extends Super
{
protected Sub(){}
public Sub(A a) { }
}
It would because its
Sane behaviour - Behaves as specified by the programmer, and it also follows the concept of inheritance in OOP languages
Following its C++ legacy
I'm putting this out as a technical reason for this behavior; I'm in agreement with several other answers that this could be semantically confusing.
Consider the following example:
public class A { }
public abstract class Super {
protected Super() {
// perform time consuming, destructive, or
// otherwise one-time operations
}
public Super(A a) {
this();
// perform A-related construction operations
}
}
public class Sub extends Super { }
public class Consumer {
public Consumer() {
Sub sub = new Sub(new A());
}
}
When the Sub is constructed, in this case the default constructor on Sub would be called, which would chain to the default constructor on Super (because that's the magic the language defines). Then, the call to Super(A) would invoke logic that is designed to be run once, at construction. This is obviously not what the developer intends.
Even without the this() call in the Super(A) constructor, the developer's intention cannot be determined; the constructors may be mutually exclusive for some reason.

What is the fragile base class problem?

What is the fragile base class problem in java?
A fragile base class is a common problem with inheritance, which applies to Java and any other language which supports inheritance.
In a nutshell, the base class is the class you are inheriting from, and it is often called fragile because changes to this class can have unexpected results in the classes that inherit from it.
There are few methods of mitigating this; but no straightforward method to entirely avoid it while still using inheritance. You can prevent other classes inheriting from a class by labelling the class declaration as final in Java.
A best practice to avoid the worst of these problems is to label all classes as final unless you are specifically intending to inherit from them. For those to intend to inherit from, design them as if you were designing an API: hide all the implementation details; be strict about what you emit and careful about what you accept, and document the expected behaviour of the class in detail.
A base class is called fragile when changes made to it break a derived class.
class Base{
protected int x;
protected void m(){
x++;
}
protected void n(){
x++; // <- defect
m();
}
}
class Sub extends Base{
protected void m(){
n();
}
}
It is widely described in below article By Allen Holub on JavaWorld
Why extends is evil.
Improve your code by replacing concrete base classes with interfaces
All of what Colin Pickard said is true, but here I want to add some of the best practices when you are writing code that may cause this kind of issue, and especially if you are creating a framework or a library.
Make all your concrete classes final by default, because you probably don't want them to be inherited. You can find this behavior as a feature of many languages, such as Kotlin. Besides, if you need to extend it, you can always remove the final keyword. In this way the absence of final on a class can be interpreted as a warning to not rely on specific functionality for other methods on this that are not private and/or final.
For classes that cannot be marked as final, make as many methods as possible final to ensure they're not modified by subclasses. Additionally, do not expose methods that are not meant to be overridden—prefer private over protected. Assume that any method not private and/or final will be overridden and ensure that your superclass code will still work.
Try to not use an inheritance ("Bar is a Foo") relationship. Instead use a helper ("Bar uses a Foo") relationship between your classes. Use interfaces rather than abstract classes to ensure that classes using this helper have a uniform interface.
Remember that almost* every extends can be replaced by implements. This is true event when you want to have a default implementation; an example of such a conversion is shown below:
Old Code:
class Superclass {
void foo() {
// implementation
}
void bar() {
// implementation
}
}
class Subclass extends Superclass {
// don't override `foo`
// override `bar`
#Override
void bar() {
// new implementation
}
}
New Code:
// Replace the superclass with an interface.
public interface IClass {
void foo();
void bar();
}
// Put any implementation in another, final class.
final class Superclass implements IClass {
public void foo() {
// implementation for superclass
}
public void bar() {
// implementation for superclass
}
}
// Instead of `extend`ing the superclass and overriding methods,
// use an instance of the implementation class as a helper.
// Naturally, the subclass can also forgo the helper and
// implement all the methods for itself.
class Subclass implements IClass {
private Superclass helper = new Superclass();
// Don't override `foo`.
public void foo() {
this.helper.foo();
}
// Override `bar`.
public void bar() {
// Don't call helper; equivalent of an override.
// Or, do call helper, but the helper's methods are
// guaranteed to be its own rather than possibly
// being overridden by yours.
}
}
The advantage of this is that the methods of the superclass are able to be sure they are working with one another, but at the same time you can override methods in your subclass.
*If you actually wanted the superclass to use your overridden method you are out of luck using this approach unless you also want to reimplement all of those methods on the "subclass". That said, the superclass calling the subclass can be confusing so it may be good to reevaluate that type of usage, its incompatibility with this approach notwithstanding.

Class inheritance problem in Java, constructors with and without params

I'm learning Java (2nd year IT student) and I'm having a little problem. With inheritance to be precise. Here's the code:
class Bazowa
{
public Bazowa(int i)
{
System.out.println("konstruktor bazowy 1");
}
public Bazowa(int j, int k)
{
System.out.println("konstruktor bazowy 2");
}
}
class Pochodna extends Bazowa
{
/* Pochodna()
{
System.out.println("konstruktor pochodny bez parametru");
} */
Pochodna(int i)
{
super(i);
System.out.println("konstruktor pochodny z parametrem");
}
}
So, the Pochodna class extends the Bazowa class, and my exercise is to make a superclass that has only constructors with parameters and a subclass that has both types (with and without).
When I comment the first constructor in Pochodna class, everything works fine, but I don't really know how to make it work without commenting that part. I guess that I have to somehow call the constructor from the first one, but don't have an idea how to do that.
Any help would be appreciated,
Paul
Your first constructor from Pochodna calls by default super(), a constructor which you do not have in Bazowa.
You should either call one of the base constructors with 1 or 2 params in Pochodna(), or create a constructor with no parameters in your base class.
EDIT: Since you said you are learning Java, I will add some extra explanations to my answer.
Every class must have a constructor, so when you do not declare one explicitly, the compiler does so for you, creating a default constructor with no parameters. This constructor won’t be added if YOU declare constructors explicitly.
In inheritance, the child class is a “specialization” of the parent. That means that the child class contains the attributes and behavior of the parent class and extends on them. But you do not declare the parent elements again (unless you really want to overwrite stuff). So, when you create an instance of the child, somehow the elements taken from the parent must also be initialized. For this you have the super(...) construct.
The first thing that must be in a child constructor is a call to super(...) so that the elements taken from the parent are properly initialized before the child tries to do something with them (you can also have a call to another of child’s constructor this(...) – in this case, the last child constructor in the calling chain will call super(...) ).
Because of this, the compiler will again add a default call to super() – with no parameters – for you when you do not do so yourself in the child.
In the first constructor of Pochodna, since you did not call super(i) or super(j, k) yourself, a call to super() was placed by default. But in the parent class you explicitly specified constructors, so the default was not created by the compiler. And from here the exception, you end up calling a constructor that does not exist.
Hope this makes it easier to learn Inheritance. Cheers.
You need to specify something like this:
Pochodna()
{
super(0);
}
The trick here is that since you specify a constructor for the superclass the compiler doesn't create a no-arg constructor for you. When you make your zero-arg constructor in the superclass it tries to call the no-arg constructor in the subclass and fails to find anything.
In short, calling another constructor (either in the superclass or in the same class) in your constructor is not optional. Either you specify another constructor explicitly or a call to the superclass' zero-arg constructor will get inserted.
Since the base class does not have a parameterless constructor, you will need to call a constructor explicitly using super, providing some kind of a default value.
For example:
Pochodna()
{
super(0);
System.out.println("konstruktor pochodny bez parametru");
}
Alternatively, you can create a protected parameterless constructor in the base class. It will not be directly accessible from the outside, but derived classes will be able to use it.
Other answers handle the call to the super constructor.
Note that you could also do this:
Pochodna() {
this(0);
System.out.println("konstruktor pochodny bez parametru");
}
which would call your other constructor for Pochodna. Just another option. Study the output to understand what is happening.
As the default constructor is not present in the parent, you have to call the other constructor in the child constructor:
Pochodna() {
super(10);
}

Java Constructor Inheritance

I was wondering why in java constructors are not inherited? You know when you have a class like this:
public class Super {
public Super(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
this.serviceA = serviceA;
//etc
}
}
Later when you inherit from Super, java will complain that there is no default constructor defined. The solution is obviously something like:
public class Son extends Super{
public Son(ServiceA serviceA, ServiceB serviceB, ServiceC serviceC){
super(serviceA,serviceB,serviceC);
}
}
This code is repetitive, not DRY and useless (IMHO)... so that brings the question again:
Why java doesn't support constructor inheritance? Is there any benefit in not allowing this inheritance?
Suppose constructors were inherited... then because every class eventually derives from Object, every class would end up with a parameterless constructor. That's a bad idea. What exactly would you expect:
FileInputStream stream = new FileInputStream();
to do?
Now potentially there should be a way of easily creating the "pass-through" constructors which are fairly common, but I don't think it should be the default. The parameters needed to construct a subclass are often different from those required by the superclass.
When you inherit from Super this is what in reality happens:
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();
}
}
So, that is the reason, because you have to call your unique constructor, since"Super" doesn't have a default one.
Now, trying to guess why Java doesn't support constructor inheritance, probably because a constructor only makes sense if it's talking about concrete instances, and you shouldn't be able to create an instance of something when you don't know how it's defined (by polymorphism).
Because constructing your subclass object may be done in a different way from how your superclass is constructed. You may not want clients of the subclass to be able to call certain constructors available in the superclass.
A silly example:
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
Or even simpler:
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
From this example, you see that you'd need some way of declaring that "I want to inherit these constructors" or "I want to inherit all constructors except for these", and then you'd also have to specify a default constructor inheritance preference just in case someone adds a new constructor in the superclass... or you could just require that you repeat the constructors from the superclass if you want to "inherit" them, which arguably is the more obvious way of doing it.
Because constructors are an implementation detail - they're not something that a user of an interface/superclass can actually invoke at all. By the time they get an instance, it's already been constructed; and vice-versa, at the time you construct an object there's by definition no variable it's currently assigned to.
Think about what it would mean to force all subclasses to have an inherited constructor. I argue it's clearer to pass the variables in directly than for the class to "magically" have a constructor with a certain number of arguments just because it's parent does.
Constructors are not polymorphic.
When dealing with already constructed classes, you could be dealing with the declared type of the object, or any of its subclasses. That's what inheritance is useful for.
Constructor are always called on the specific type,eg new String(). Hypothetical subclasses have no role in this.
David's answer is correct. I'd like to add that you might be getting a sign from God that your design is messed up, and that "Son" ought not to be a subclass of "Super", but that, instead, Super has some implementation detail best expressed by having the functionality that Son provides, as a strategy of sorts.
EDIT: Jon Skeet's answer is awesomest.
Because a (super)class must have complete control over how it is constructed. If the programmer decides that it doesn't make sense to provide a default (no args) constructor as part of the class's contract, then the compiler should not provide one.
You essentially do inherit the constuctors in the sense that you can simply call super if and when appropriate, it's just that it would be error prone for reasons others have mentioned if it happened by default. The compiler can't presume when it is appropriate and when it isn't.
The job of the compiler is to provide as much flexibility as possible while reducing complexity and risk of unintended side-effects.
I don't know any language where subclasses inherit constructors (but then, I am not much of a programming polyglott).
Here's a discussion about the same question concerning C#. The general consensus seems to be that it would complicate the language, introduce the potential for nasty side effects to changes in a base class, and generally shouldn't be necessary in a good design.
A derived class is not the the same class as its base class and you may or may not care whether any members of the base class are initialized at the time of the construction of the derived class. That is a determination made by the programmer not by the compiler.

Categories

Resources