This question already has answers here:
What's wrong with overridable method calls in constructors?
(8 answers)
Closed 2 years ago.
I have the follow classes:
public abstract class MyAbstractClass {
protected int x;
protected int number;
public MyAbstractClass(int x) {
this.x = x;
this.number = this.generateNumber();
}
public abstract int generateNumber(); // This class is called in the super constructor and elsewhere
}
public class MySubClass extends MyAbstractClass {
private int y;
public MySubClass(int x, int y) {
super(x);
this.y = y;
}
#Override
public int generateNumber() {
return this.x + this.y; // this.y has not been initialized!
}
}
My issue is that MySubClass's y property has to be initialized before the super constructor is run, because a method using y is run in the super constructor.
I am aware that this may not be possible even with some sneaky workaround, but I am yet to find an alternative solution.
Also please keep in mind that I will have many more derived classes, with different values being passed into their constructors.
You can defer the number calculation until it is needed.
public abstract class MyAbstractClass {
protected int x;
protected Integer number;
public MyAbstractClass(int x) {
this.x = x;
}
public int getNumber() {
if (number == null) {
number = generateNumber();
}
return number.intValue();
}
protected abstract int generateNumber();
}
Related
I am trying to make a videogame that is similar to "Turing Complete". In this game you are supposed to connect circuit pieces to create a computer by the end. So I have a question on inheritance. I have a class called "Circuitware" and every object extends this as follows
public Circuitware implements connectable {
protected int x;
protected int y;
protected int allowedConnetions;
protected Ciruitware[] connections;
private int totalConnections;
public Circuitware(int x, int y, int allowedConnections, Circuitware[] connections) {
this.x = x;
this.y = y;
this.allowedConnections = allowedConnections;
this.connections = new Array[allowedConnections];
for (Object O: connections) {
if (O != null) {
this.totalConnections += 1;
}
}
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public<T> void addConnection(T connection) {
if (this.totalConnections<this.allowedConnections) {
this.connections.add(connection);
}
}
}
Now an example of a subclass is
public class LogicGate extends Circuitware {
String type;
public LogicGate(int x, int y, int allowedConnections, Circuitware[] connections, String type) {
super(x, y, allowedConnections, connections);
this.type = type;
}
public boolean output() {
//Depending on the gate and input we output things
}
}
Now the thing is that the number of allowed connections depends on the type of the circuit, for example logic gates in my game can have 1 connection if it is an NOT gate, 2 connections if it is an AND, XOR, NOR gates or 3 connections if it is and extended OR, extended AND gates, where are switches can only have 0. Is there are better way of doing this?
I also need help understanding that if I have an array of type Circuitware can I add objects from its subclasses? Because the output of logic gates and inputs of other logic gates while they can be inputs to clocks.
Would it be better, design-wise, if I got rid of the "Circuitware" class altogether? But then wouldn't that make connections between different classes harder?
public class A {
final int x;
public A(int x) {
if ( this instanceof B ) {
if(x > 5)
this.x = x;
else
this.x = 0;
} else {
this.x = 0;
}
}
}
public class B extends A {
public B(int x) {
super(x);
}
}
I want to put the if in the class B to avoid instanceof ( because i have more sub classes and the x value depends on the subClass ), but when i do that i get compiler error: Constructor call must be the first statement in a constructor!
Can you help me avoid instanceof?
There are two ways we can initialize constants, first one is to initialize them in place on the same line as in the answer by Adam, second is to use constructors, which you're trying to implement.
Using inline initialization is generally more flexible because we are not bound to the rules of the constructor, like this(...) or super(...) call should be the first in the constructor. However if you do want to use constructor for the purpose you can use method containing logic as inline call as argument to the this(...) or super(...). This method should be static as the instance of the class doesn't exist yet as we're in constructor. Following is a simple solution for the same.
class A {
final int x;
public A(int x) {
this.x = 0;
}
}
class B extends A {
public B(int x) {
super(getValueForX(x));
}
private static int getValueForX(int x) {
return x > 5 ? x : 0;
}
}
Just create an abstract method in A and implement it in your derived classes like this:
public abstract class A {
final int x;
abstract int calculateX(int x);
public A(int x) {
this.x = calculateX(x);
}
}
public class B extends A {
#Override
int calculateX(int x) {
return x + 1;
}
public B(int x) {
super(x);
}
}
I have this code:
class A {
protected int x;
public int getX() {
return x;
}
}
class B extends A {
private int answer;
public void someMethod() {
answer = x;
answer = this.x;
answer = this.getX();
answer = super.x;
answer = super.getX();
}
}
Which of the assignment statements in someMethod are valid?
I'm thinking that the two 'super' lines are correct?
Which of the assignment statements in someMethod are valid?
All of them.
x, this.x and super.x all point to protected int x in class A which is visible to the subclass B. this.getX() and super.getX() both call public int getX() in class A which is visible to the subclass B.
answer, x and the return value of getX() are all of type int, so the assignment is valid.
(sorry for the pun)
Say one wants to define a generic builder, like this:
public abstract class GenericBuilder<T extends Product> {
int x;
int y;
<K extends GenericBuilder<T>> K setX(int x) {
this.x = x;
return (K)this;
}
<K extends GenericBuilder<T>> K setY(int y) {
this.y = y;
return (K) this;
}
abstract T build();
}
abstract class Product {
int x;
int y;
}
class ConcreteProduct extends Product {
int z;
}
class ConcreteBuilder extends GenericBuilder<ConcreteProduct>{
int z;
<K extends GenericBuilder<ConcreteProduct>> K setZ(int z) {
this.z = z;
return (K) this;
}
#Override
ConcreteProduct build() {
ConcreteProduct cp = new ConcreteProduct();
cp.x = x;
cp.y = y;
cp.z = z;
return cp;
}
public static void main(String[] args) {
new ConcreteBuilder().setX(1).setY(2).setZ(3);
}
}
When calling ConcreteBuilder.setZ(), it fails during compilation.
Why is that? Is it due erasure? Or the generics, say, don't carry information about its generic parameters?
EDIT:
Any ideas how to avoid using second generic parameter in:
public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder>
i.e. <..., ConcreteBuilder>, which seems to be a little clumsy? I guess it's not possible. Are there other languages (C# maybe?) which allow to do that?
Break your code this way and you will understand that your class GenericBuilder<ConcreteProduct> doesn't have any setZ() method defined.
GenericBuilder<ConcreteProduct> setY = new ConcreteBuilder().setX(1).setY(2);
setY.setZ(3);
In your GenericBuilder your functions return a GenericBuilder when you don't specify the type argument of the function. In your main function the call to setX returns a GenericBuilder and you loose the information that you are actually using a ConcreteBuilder. To succesfully make the calls, you have to specify the generic parameters for the setters:
new ConcreteBuilder().<ConcreteBuilder>setX(1).<ConcreteBuilder>setY(2).setZ(3);
Alternative
You can add a second type parameter to GenericBuilder:
public abstract class GenericBuilder<T extends Product, K extends GenericBuilder<T, K>> {
int x;
int y;
K setX(int x) {
this.x = x;
return (K)this;
}
K setY(int y) {
this.y = y;
return (K) this;
}
abstract T build();
}
and change ConcreteBuilder to this:
public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder> {
int z;
ConcreteBuilder setZ(int z) {
this.z = z;
return this;
}
#Override
public ConcreteProduct build() {
ConcreteProduct cp = new ConcreteProduct();
cp.x = x;
cp.y = y;
cp.z = z;
return cp;
}
public static void main(String[] args) {
new ConcreteBuilder().setX(1).setY(2).setZ(3);
}
}
I'm trying to compile this in java and get this error: error: constructor Miclass in class Miclass cannot be applied to given types.
what's happening?
class Miclass {
public int x;
private int y;
protected int z;
public Miclass(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public class A extends Miclass {
public static void main(String [] args) {
Miclass m_class = new Miclass(2, 4, 8);
System.out.println("m_class.x = " + m_class.x);
System.out.println("m_class.y = " + m_class.y);
System.out.println("m_class.z = " + m_class.z);
}
}
As there is already a constructor defined in Miclass you need to add a constructor in A that invokes this constructor in the super-class:
public class A extends Miclass {
public A(int x, int y, int z) {
super(x, y, z);
}
// methods/fields specific to `A`
...
}
The code will not compile after this change as the variable y is not visible to A. To allow the code to compile you can add a getter method to access the variable.
I have correct your code, try this, and read my explanation after..
class Miclass {
public int x;
private int y;
protected int z;
public Miclass(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getY()
{
return y;
}
}
public class A {
public static void main(String [] args) {
Miclass m_class = new Miclass(2, 4, 8);
System.out.println("m_class.x = " + m_class.x);
System.out.println("m_class.y = " + m_class.getY());
System.out.println("m_class.z = " + m_class.z);
}
}
Java provides default no-arg constructor only when there is no other constructor defined
In this case you have defined a constructor with 3 int arguments for class Miclass and you have not called it from class A which extends Miclass
due to this, when a default no-argument constructor will be provided to A class, a call to super no-argument will be done which is not possible because you do not have a no-argument constructor in Miclass
to solve these issues you will either have to create a no-arg constructor in superclass or create a constructor with 3 int arguments.
also your variable is not visible in class A because private variables are visible only in the same class