I have a project where I need to convert the value of x into 1+5 and calculate it to 6.
I have no problem if x is the only value that needs to be converted, but if I have (5*x) it becomes an issue. Rather than doing 5*(1+5) and calculate it to 30, it simply tries to compute 5*x and this results to an exception that says it cannot compute because a variable is present.
The ExpFoo class is an interface and it's the superclass of TimesExpFoo, PlusFoo, IntFoo and VarFoo. Replacement class is completely separated.
TimesExpFoo multiplies the number and PlusFoo adds the numbers.
Here's the Main class. This gets the varfoo and numbers. The Replacement class puts the varfoo and numbers in a hashmap that's used by the Replacement class. The varfoo is the key and the numbers are the values.
ExpFoo x = new VarFoo("x");
ExpFoo e1 = new IntFoo(1);
ExpFoo e2 = new IntFoo(2);
ExpFoo e5 = new IntFoo(5);
ExpFoo times = new TimesExpFoo(e5, x);
Replacement s = new Replacement();
s.put(new VarFoo("x"), new PlusExpFoo(e1, e5));
times.computeValue(s);
System.out.println(times.computeValue(s));
It will then go to ExpFoo class using computeValue method, the subst contains [x:=1+5], it will first check the applyReplacement method:
default int computeValue(Replacement subst) {
ExpFoo specialised = applyReplacement(subst);
return specialised.computeValue();
}
It will then go to TimesExpFoo class using applyReplacement method, it will return [x:=1+5]:
@Override
public ExpFoo applyReplacement(Replacement s) {
return this;
}
It will go to back ExpFoo class, this time specialised has (5*x) and subst has [x:=1+5], it will unfortunately return specialised value which is (5*x):
default int computeValue(Replacement subst) {
ExpFoo specialised = applyReplacement(subst);
return specialised.computeValue();
}
It will then go to TimesExpFoo's computeValue method, the getLeft method contains 5 and getRight method contains x:
@Override
public int computeValue() {
return getLeft().computeValue() * getRight().computeValue();
}
Which will finally go to the VarFoo class and use the computeValue method which will throw an error that it cannot compute because of a variable present.
I'm aware that TimesExpFoo's applyReplacement method returns the value of subst itself and I could do more with it but I'm not sure how it works. I've tried using return s.get((VarFoo) getRight()) but it will just give me a casting error TimesExpFoo cannot be cast to class VarFoo.
My VarFoo class overrides the equals and hashcodes methods. If the varfoo, x, has the same hashcode and it should overwrite 5*x with 5*(1+5). It has no problem doing it on its own.
I'm puzzled by the fact that it wouldn't overwrite the x variable. Does it has something to do with the hashmap?
My applyReplacement method is just a signature in the Expression class, so I doubt that's the issue.
Here's the Replacement class which uses hashmap:
public class Replacement {
private Map<VarFoo, ExpFoo> replacementMap;
public Replacement() {
replacementMap = new HashMap<>();
}
public ExpFoo put(VarFoo var, ExpFoo exp) {
if(replacementMap.containsKey(null) || replacementMap.containsValue(null)){
throw new NullPointerException();
}
return replacementMap.put(var, exp);
}
public boolean forget(VarFoo var) {
if(replacementMap.containsKey(null)) {
throw new NullPointerException();
}
else {
if(!replacementMap.containsKey(var))
return true;
}
return false;
}
public ExpFoo get(VarFoo var) {
if(replacementMap.containsKey(null)){
throw new NullPointerException();
}
return replacementMap.get(var);
}
public boolean hasMappingFor(VarFoo var) {
if(replacementMap.containsKey(var)){
return true;
}
else if(replacementMap.containsKey(null)){
throw new NullPointerException();
}
return false;
}
}
My VarFoo's equals and hashcode methods, it uses the instance variable name which is a String:
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof VarFoo))
return false;
if (o == this)
return true;
return name.equals(((VarFoo) o).name);
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
The TimesExpFoo class is a subclass of BinaryExpFoo which is an abstract class and it's the subclass of ExpFoo. The BinaryExpFoo is the one that gives the left, right and operatorSymbol instance variables.
public class TimesExpFoo extends BinaryExpFoo {
public TimesExpFoo(ExpFoo left, ExpFoo right) {
super(left, right, "*");
}
@Override
public int computeValue() {
return getLeft().computeValue() * getRight().computeValue();
}
@Override
public ExpFoo applyReplacement(Replacement s) {
return this;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof TimesExpFoo)) {
return false;
}
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode();
}
containsKey(null)andcontainsValue(null)doesn't make any sense. You want to check the arguments given to the method, not the values inside yourMap. UseObjects.requireNonNull()to ensure that the arguments are notnull.applyReplacement()doesn't do anything. I guess it is supposed to replace the variables, but nothing is happening there (besides returningthis). Please elaborate on this.