I need help with a problem I have with generics in Java. I'm writing this Computer algebra system, where the user enters a math expression and the system works with it in different ways (expand it, simplify it etc). It worked fine for expressions containing natural numbers, and I wanted to expand it to work with mathematical sets as well. Instead of +, you would have the intersection operator, etc.
At first, I started recording everything for the sets, but then I realized this was probably not good and started using generics.
Instead of having one parse tree like MathExpr and one like SetExpr, I thought I could just have a generic Expression<T> and build a base class Number and a base class Set.
To try to clarify, I want a mathematical expression like (2 * a) + (3 + 2) to be an instance of a class Expression<Number> and a set expression like (A ∪ B) ∩ C to be an instance of Expression<Set>. I can then perform different operations on this, like calculate the depth etc.
The + operation is implemented as one class, the * as one class etc. Both these classes are subclasses of an abstract class called TwoExpr which in turn is a subclass of the abstract class Expr. This is how I have done it now and everything works fine.
When I wanted to change my code I made my Expr class generic. That is Expr<T>. I also changed TwoExpr to TwoExpr<T> and created a base class Number.
The problem is, now I can't seem to instantiate objects of the type Sum<Number>.
I get a "Type mismatch: cannot convert from Sum to Expr<Number>" error. But Sum is a subclass of TwoExpr<Number>, which in turn is a subclass of Expr<Number>. As you may realize, I can't make the class Sum generic and call it Sum<Number>, because all arithmetic operations don't have analogues for sets.
I have always been able to create objects like
Expr zero= new Leaf(0);
Variable a = new Variable("a");
Expr aPlusZero = new Sum(a, zero);
When I changed to generics, the same code looks like this:
Expr<Number> zero= new Leaf<Number>(new Number(0)); //works fine
Variable<Number> a = new Variable<Number>("a"); //works fine
Expr<Number> APlusZero=new Sum(a,zero); //gives a "Type mismatch:
//cannot convert from Sum to Expr<Number>" error
How come it doesn't recognize that Sum(a,zero) is a subclass of Expr<Number>, when it says in the declaration of Sum
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
...
}
and in the declaration of TwoExpr
public abstract class TwoExpr<T> extends Expr<T> {
protected Expr<T> a;
protected Expr<T> b;
public TwoExpr(Expr<T> a, Expr<T> b) {
this.a=a;
this.b=b;
}
...
}
I know that Lizkows substitution principle doesn’t apply for generic arguments. But Number isn’t a subclass of anything (except Object) and don't have any subclasses.
I hope I've been fairly clear about what I'm trying to do and what problem I have. Does anybody have any idea how to solve it? Please tell me if anything was unclear in the above or if you want more code.
Thanks in advance.
Mattias
I think your problem is in the classes you did not show, I tried the following and it works:
Expr<Number> zero= new Expr<Number>();
Expr<Number> a= new Expr<Number>();
Expr<Number> APlusZero=new Sum(a,zero);
Might it be that Variable is not an Expr?
UPDATE:
I played a little creating Variable and Leaf as I imagine them and it all works:
public class Number {
public Number(int i){}
}
public class Variable<T> extends Expr<T> {
public Variable(String s){}
}
public class Leaf<T> extends Expr<T> {
public Leaf(T t) {
super();
}
}
public class Expr<T> {
}
public class TwoExpr<T> extends Expr<T> {
public TwoExpr(Expr<T> a, Expr<T> b) {
}
}
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
}
public class AllTogether {
public static void main(String[] args) {
Expr<Number> zero= new Leaf<Number>(new Number(0));
Variable<Number> a = new Variable<Number>("a");
Expr<Number> APlusZero=new Sum(a,zero);
}
}
If you take the extends Expr from Variable it does give the error you experience, might it be this the cause?
Maybe try to debug it:
Object = new Sum(a,zero);
System.out.println(o.getClass().getGenericSuperclass());
Furthermore maybe is a better solution for your system.
Sum does not import Types.Number. So it's not Expr<Types.Number> but Expr<java.lang.Number>. I would assume this would give a compilation error not only on the assignment but also on the construction of new Sum(vA, zero), but maybe the compiler sees the other error first.
Related
I am trying to create a generic class in Java that will perform operations on numbers. In the following example, addition, as follows:
public class Example <T extends Number> {
public T add(T a, T b){
return a + b;
}
}
Forgive my naivety as I am relatively new to Java Generics. This code fails to compile with the error:
The operator + is undefined for the argument type(s) T, T
I thought that with the addition of "extends Number" the code would compile. Is it possible to do this Java or will I have to create overridden methods for each Number type?
Number does not have a + operator associated with it, nor can it since there is no operator overloading.
It would be nice though.
Basically, you are asking java to autobox a descedant of Number which happens to include Integer, Float and Double, that could be autoboxed and have a plus operator applied, however, there could be any number of other unknown descendants of Number that cannot be autoboxed, and this cannot be known until runtime. (Damn erasure)
Your problem is not really related to generics, rather to operators, primitives vs objects, and autoboxing.
Think about this:
public static void main(String[] args) {
Number a = new Integer(2);
Number b = new Integer(3);
Number c = a + b;
}
The above does not compile
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
Number c = a + b;
}
The above does compile, but only because of autoboxing - which is kind of a hacky syntax glue introduced in Java 5, and only works (in compile time) with some concrete types : int-Integer for example.
Behind the scenes, the Java compiler is rewriting the last statement ("I must unbox a and b to apply the sum operator with primitive datatypes, and box the result to assign it to object c") thus:
Number c = Integer.valueOf( a.intValue() + b.intValue() );
Java can't unbox a Number because it does not know at compile time the concrete type and hence it cannot guess its primitive counterpart.
You can do something like this
class Example <T extends Number> {
public Number add(T a, T b){
return new Double(a.doubleValue() + b.doubleValue());
}
}
Yes, Nathan is correct. If you want something like this, you have to write it yourself
public class Example <T extends Number> {
private final Calculator<T> calc;
public Example(Calculator<T> calc) {
this.calc = calc;
}
public T add(T a, T b){
return calc.add(a,b);
}
}
public interface Calculator<T extends Number> {
public T add(T a, T b);
}
public class IntCalc implements Calculator<Integer> {
public final static IntCalc INSTANCE = new IntCalc();
private IntCalc(){}
public Integer add(Integer a, Integer b) { return a + b; }
}
...
Example<Integer> ex = new Example<Integer>(IntCalc.INSTANCE);
System.out.println(ex.add(12,13));
Too bad Java has no type classes (Haskell) or implicit objects (Scala), this task would be a perfect use case...
There are similar questions to this one, and the answer is you can't do it like that.
You could check if a and b are an instance of Long/Double/Integer/etc. and delegate the add to methods like:
public Integer add(Integer a, Integer b) {
return a+b; // this actually uses auto boxing and unboxing to int
}
And you would need to create one for every type that extends Number, so that's not really feasible. In other words, don't use generics for numeric operations. Number as a superclass is pretty limited.
Consider Example<Number>, how would + work on that? There is no add or similar in Number or even the likes of Integer.
Worse consider final class FunkyNumber extends Number { ... weird stuff, no add op ... }.
Even the java runtime library has this problem, most of the methods dealing with primitives have to duplicate the same functionality.
The fastest option would be to write your code for one type and then copy it and replace the type to generate the methods for the other types. A short script should be enough to do this.
This question already has answers here:
why we can't override a method and define it to return a superclass of the original method?
(3 answers)
Closed 3 years ago.
PLEASE NOTE - I am asking WHY? It would be very useful if you could give an example where changing the return type actually breaks the code
why can't I change the return type of an overridden method (other than covariant return types).
class Parent{
public void sayhello(){ ... };
}
class Child extends Parent{
public String sayhello() { . . .}
}
Now if I run the following code:
class test{
public static void main(String[] args){
Parent p = new Child();
p.sayHello();
}
}
Cam someone please confirm if the following steps are happening:
Compiler finds out the type of object 'p' which is Parent.
Compiler checks if method 'sayHello()' is present in Parent class.
During Runtime, JVM finds out that it is a Child object and calls child version of the method.
Child method is called.
Thanks.
Let's use a simple example to explain why it doesn't make any sense to change the return type of an overridden method.
Suppose I have a Car object:
class Car {
public String getModel() {
return "Awesome Car";
}
}
This Car class has a method getModel() that returns a String.
Now, you have another class that extends Car and overrides the getModel() method:
class DumbCar extends Car {
#Override
public Hamburger getModel() {
return new Hamburger();
}
}
Suddenly, you have a major problem. You create a DumbCar object and since you know that all Car objects can tell you their model, you try to get that model:
DumbCar myCar = new DumbCar();
System.out.println(myCar.getModel());
The output is A juicy Big Mac!
Does that make any sense to you? You cannot drive a Big Mac.
Java is a heavily type-safe language. When you write a statement asking to getModel(), you need to be absolutely, 100% positive, that the data you get back is what you were expecting. Java enforces that expectation.
Java is a statically typed language.
This means that the compiler checks that all types make sense before the program is even run. So you will not get errors at runtime because some method or field is not "there".
In order for this to work if you have code that says
MyBean x = something.getMyBean();
it cannot be allowed for a subclass of what the compiler has determined the type of something to be to change the return type of getMyBean() to something other than MyBean (subclasses of MyBean are also allowed, this is called narrowing the return type -- but even that was not possible before Java 5).
The problem is basically that such a thing would make the Java type system unsound, and since Java has a statically-typed system this cannot be allowed.
Suppose you had an Expression interface:
interface Expression {
Integer evaluate();
}
And now you have an Addition implementation:
class Addition implements Expression {
private Expression left;
private Expression right;
Addition(Expression left, Expression right) {
this.left = left;
this.right = right;
}
#Override
public Integer evaluate() {
return left.evaluate() + right.evaluate();
}
}
This works as long as expressions evaluate to Integers, e.g.
class Constant implements Expression {
private Integer value;
Constant(Integer value) {
this.value = value;
}
#Override
public Integer evaluate() {
return this.value;
}
}
Which allows us to do things like:
Expression left = new Constant(1);
Expression right = new Constant(2);
Expression addition = new Addition(left, right);
Integer result = addition.evaluate();
What would happen now if you had an expression that instead of evaluating to an Integer evaluated to something else that is not an expression, like a Cat or a Dog?
It would immediately break the soundness of every other expression you had written in the past like that of the last example or the obvious assumptions we made in the Addition.evaluate method where we assumed that left and right expressions returned Integer not Cats or Dogs.
I'm trying to generalise some code by iterating over all constants of an enum to receive the same specific argument from each one.
Specifically I have an enum P with some constants A,B,C.
Each of these constants is itself an enum and implements an interface I that defines a function f.
P.values() gives me an array P[] A = {A,B,C}, however I can't call A[i].f() since A[i] is of course of type P which doesn't implement I.
Now in my understanding a function can return an interface, but I can not instantiate it and therefore can't cast to it.
Should I overwrite values() for P to return I[]? If so, how would I do that since I can't cast to I? Or is there another solution?
I am working in eclipse but assuming that it's complaints are indicative of a true mistake, not just eclipse not recognising types.
Since I'm somewhat new to Java I would also appreciate any links to resources that explain the underlying rules of type matching/checking.
This seems to do what you describe - perhaps I have misunderstood your question though. If so please explain further.
interface I {
void f ();
}
enum P implements I{
A,
B,
C {
// Demonstrate enum-specific implementation.
#Override
public void f () {
System.out.println("SEEEEEE!");
}
};
// By default `f` prints the name of the enum.
#Override
public void f () {
System.out.println(name());
}
}
public void test() throws Exception {
for ( I i : P.values()) {
i.f();
}
}
I was reading an open-source code, and there was a constructor designed like this:
public class FeatureSequence2FeatureVector extends Pipe implements Serializable
{
boolean binary;
public FeatureSequence2FeatureVector (boolean binary)
{
this.binary = binary;
}
public FeatureSequence2FeatureVector ()
{
this (false);
}
}
This may be just a trivial preference matter, but what I would do is like this:
public class FeatureSequence2FeatureVector extends Pipe implements Serializable
{
boolean binary = false;
public FeatureSequence2FeatureVector (boolean binary)
{
this.binary = binary;
}
public FeatureSequence2FeatureVector ()
{
}
}
Is there any possible negative outcome by assigning an initial value for class variables?
Would the two ways be almost equally preferred?
These two ways are not equally preferred.
The original way makes sure that all initialization goes through a primary constructor. The second way allows different paths for initializing an object.
In your example it's pretty trivial. But with the second way one constructor could be modified to do something different from how the other constructor did things, whereupon how your objects are initialized depends on which constructor was chosen.
This question shows a situation where allowing different paths caused trouble.
One reason I've seen developers do this is for maintainability and future-proofing.
Let's break it down into a different application:
public void foo() {
this.foo(1);
}
public void foo(int a) {
this.foo(a, 2, 3);
}
public void foo(int a, int b, int c) {
// ...
}
foo is assumed to do the exact, or a similar, operation - regardless of the overload. However, what if that functionality were to change? In your example, you'd have to change the functionality for both versions, whereas in the above example, only foo(int, int, int) would have to be changed.
Future-proofing is something that is taken into account in the design of an API, and the above design pattern is adopted frequently due to the ability to maintain one block of code versus 2 or 3 (or however many overloads you have).
Constructors are no different, other than that they are invoked with this(...).
I am trying to create a generic class in Java that will perform operations on numbers. In the following example, addition, as follows:
public class Example <T extends Number> {
public T add(T a, T b){
return a + b;
}
}
Forgive my naivety as I am relatively new to Java Generics. This code fails to compile with the error:
The operator + is undefined for the argument type(s) T, T
I thought that with the addition of "extends Number" the code would compile. Is it possible to do this Java or will I have to create overridden methods for each Number type?
Number does not have a + operator associated with it, nor can it since there is no operator overloading.
It would be nice though.
Basically, you are asking java to autobox a descedant of Number which happens to include Integer, Float and Double, that could be autoboxed and have a plus operator applied, however, there could be any number of other unknown descendants of Number that cannot be autoboxed, and this cannot be known until runtime. (Damn erasure)
Your problem is not really related to generics, rather to operators, primitives vs objects, and autoboxing.
Think about this:
public static void main(String[] args) {
Number a = new Integer(2);
Number b = new Integer(3);
Number c = a + b;
}
The above does not compile
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
Number c = a + b;
}
The above does compile, but only because of autoboxing - which is kind of a hacky syntax glue introduced in Java 5, and only works (in compile time) with some concrete types : int-Integer for example.
Behind the scenes, the Java compiler is rewriting the last statement ("I must unbox a and b to apply the sum operator with primitive datatypes, and box the result to assign it to object c") thus:
Number c = Integer.valueOf( a.intValue() + b.intValue() );
Java can't unbox a Number because it does not know at compile time the concrete type and hence it cannot guess its primitive counterpart.
You can do something like this
class Example <T extends Number> {
public Number add(T a, T b){
return new Double(a.doubleValue() + b.doubleValue());
}
}
Yes, Nathan is correct. If you want something like this, you have to write it yourself
public class Example <T extends Number> {
private final Calculator<T> calc;
public Example(Calculator<T> calc) {
this.calc = calc;
}
public T add(T a, T b){
return calc.add(a,b);
}
}
public interface Calculator<T extends Number> {
public T add(T a, T b);
}
public class IntCalc implements Calculator<Integer> {
public final static IntCalc INSTANCE = new IntCalc();
private IntCalc(){}
public Integer add(Integer a, Integer b) { return a + b; }
}
...
Example<Integer> ex = new Example<Integer>(IntCalc.INSTANCE);
System.out.println(ex.add(12,13));
Too bad Java has no type classes (Haskell) or implicit objects (Scala), this task would be a perfect use case...
There are similar questions to this one, and the answer is you can't do it like that.
You could check if a and b are an instance of Long/Double/Integer/etc. and delegate the add to methods like:
public Integer add(Integer a, Integer b) {
return a+b; // this actually uses auto boxing and unboxing to int
}
And you would need to create one for every type that extends Number, so that's not really feasible. In other words, don't use generics for numeric operations. Number as a superclass is pretty limited.
Consider Example<Number>, how would + work on that? There is no add or similar in Number or even the likes of Integer.
Worse consider final class FunkyNumber extends Number { ... weird stuff, no add op ... }.
Even the java runtime library has this problem, most of the methods dealing with primitives have to duplicate the same functionality.
The fastest option would be to write your code for one type and then copy it and replace the type to generate the methods for the other types. A short script should be enough to do this.