Generic types to achieve contravariant arguments - java

Let's consider this code:
public interface Number {
public Number plus(Number n);
}
public class Complex implements Number {
private double re, im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
#Override
public Complex plus(Complex c) {
return new Complex(this.re + c.re, this.im + this.im);
}
}
It wouldn't compile because if Complex.plus() overrides Number.plus(), its argument must be exactly the same as the overridden method. I thought about using generics for the type of objects a number can interact with, but it produces a very unclean code, with unparametrized use of Number and redundancy:
public interface Number<T extends Number> {
public T plus(T n);
}
public class Complex implements Number<Complex> {
private double re, im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
#Override
public Complex plus(Complex c) {
return new Complex(this.re + c.re, this.im + this.im);
}
}
Is there a more elegant way to achieve this?
Thank you for your help.

Simple fix: make the type parameter self-bounded:
public interface Number<T extends Number<T>> {
(and then pinky-swear that you'll only ever define a classes that implement the interface for themselves, e.g. class Self implements Number<Self>)
However, I would do this without the Number interface, at least in terms of the plus method. Unless you can meaningfully add different subtypes of Number, having such a method in the common interface doesn't obviously serve a purpose.
Consider why there are no arithmetic methods defined on the standard Number interface.
Instead, don't have the plus "operator" in the Complex class either: use the standard BinaryOperator interface to define a plus operator for specific types:
BinaryOperator<Complex> complexPlus = (a, b) -> new Complex(a.re + b.re, a.im + b.im);
BinaryOperator<Integer> integerPlus = (a, b) -> a + b; // Or Integer::sum.
and then apply these:
Complex complexSum = complexPlus.apply(firstComplex, secondComplex);
Integer integerSum = integerPlus.apply(firstInt, secondInt);

Related

Java generics problem - required T, provided capture or vice-versa

I have an abstract class:
public abstract class AbstractPoint<PointType extends AbstractPoint<PointType>> {
public abstract double getX();
public abstract double getY();
public abstract double rho();
// Find the distance between two points
public final <T extends AbstractPoint<T>> double distance(T other) {
return vectorTo(other).rho();
}
// Find vector from one point to another.
public abstract <T extends AbstractPoint<T>> PointType vectorTo(T other);
// other methods ...
}
Classes OrthogonalPoint and PolarPoint extend the abstract class:
public class OrthogonalPoint extends AbstractPoint<OrthogonalPoint> {
private double x;
private double y;
public OrthogonalPoint(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() { return this.x; }
public double getY() { return this.y; }
public double rho() { return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); }
public double theta() { return Math.atan2(this.y, this.x); }
public <T extends AbstractPoint<T>> OrthogonalPoint vectorTo(T other) {
return new OrthogonalPoint(other.getX() - this.x, other.getY() - this.y);
}
}
public class PolarPoint extends AbstractPoint<PolarPoint> {
private double rho;
private double theta;
public PolarPoint(double x, double y) {
this.rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
this.theta = Math.atan2(y, x);
}
public double getX() { return this.rho() * Math.cos(this.theta()); }
public double getY() { return this.rho() * Math.sin(this.theta()); }
public double rho() { return this.rho; }
public double theta() { return this.theta; }
public <T extends AbstractPoint<T>> PolarPoint vectorTo(T other) {
return new PolarPoint(other.getX() - this.getX(), other.getY() - this.getY());
}
}
With this code, I can correctly calculate the distance between points no matter whether they are of same type (orthogonal or polar).
Now I add another class called Route:
public class Route {
private final List<? extends AbstractPoint<?>> points = new ArrayList<>();
// 'point' in 'this.points.add(point)' underlined red
public <T extends AbstractPoint<T>> void appendPoint(T point) { this.points.add(point); }
public <T extends AbstractPoint<T>> void removePoint(T point) { this.points.remove(point); }
// 'point' in '.distance(point)' underlined red
public double routeDistance() {
return this.points.stream().skip(1).mapToDouble(point ->
this.points.get(this.points.indexOf(point) - 1).distance(point)).sum();
}
}
A route is an ordered number of points whose length is defined as the sum of the distances of the points in the sequence. So, if a route consists of three points (p1, p2, and p3) the distance of the route is p1.distance(p2) + p2.distance(p3).
However, some places (look at the comments in Route) in my code are underlined red:
// in appendPoint function
Required type: capture of ? extends AbstractPoint<?>
Provided: T
// in routeDistance function
Required type: T
Provided: capture of ? extends AbstractPoint<?>
I want to be able to add any type of point to the points list and the call routeDistance function to calculate the length of the route. What is wrong with my solution - how should I change my generics (in any class)?
First, note that you are both adding data to the points list, and taking data out of the list, so you should not use extends or super. (See also: PECS)
private final List<AbstractPoint<?>> points = new ArrayList<>();
Next, you will see that there is an error at the .distance(point) call. This is because there is no type that can be used as the type parameter T of the distance method, that also satisfies the constraint T extends AbstractPoint<T>. Note that the type of point is AbstractPoint<?>, and AbstractPoint<?> can't be T, because AbstractPoint<?> does not extend AbstractPoint<AbstractPoint<?>>. See the problem?
From what I can see, the way you have written distance does not allow you to find the distance between two AbstractPoints. One of the points must be a concrete type.
In fact, distance and vectorTo doesn't actually need the generic parameter. It can just take a AbstractPoint<?>:
public final double distance(AbstractPoint<?> other) {
return vectorTo(other).rho();
}
public abstract PointType vectorTo(AbstractPoint<?> other);
This now allows you to pass in AbstractPoint<?> to distance.
Side note: your current implementation of routeDistance using indexOf is not very efficient, as it goes over the list once for every point in the list. You can do this with a zip operation instead. Using the zip method in the linked answer:
return zip(points.stream(), points.stream().skip(1), AbstractPoint::distance)
.mapToDouble(x -> x).sum();

Why is it considered bad practice to define a covariant compareTo method?

Here's an example from my code:
Baseclass:
abstract class AbstractBase implements Comparable<AbstractBase> {
private int a;
private int b;
public int compareTo(AbstractBase other) {
// compare using a and b
}
}
Implementation:
class Impl extends AbstractBase {
private int c;
public int compareTo(Impl other) {
// compare using a, b and c with c having higher impact than b in AbstractBase
}
FindBugs reports this as an issue. But why is that? What could happen?
And how would I correctly implement a solution?
Impl#compareTo(Impl) is not overriding AbstractBase#compareTo(AbstractBase) since they don't have the same signature. In other words, it won't be called when using Collections#sort for example.
EDIT: Added solution without casting
If you don't want to cast you could try the following.
Alter your baseclass to:
abstract class AbstractBase<T extends AbstractBase<?>> implements Comparable<T> {
//...
public int compareTo(T other) {
//...
}
}
And you Impl class to:
class Impl extends AbstractBase<Impl> {
//...
#Override
public int compareTo(Impl other) {
//...
}
}
Solution with casting:
A possible solution would be to override the compareTo(AbstractBase) method in the Impl class and explicitly check if an instance of Impl is passed in:
class Impl extends AbstractBase {
//...
#Override
public int compareTo(AbstractBase other) {
if (other instanceof Impl) {
int compC = Integer.compare(c, ((Impl) other).c);
if (compC == 0) {
return super.compareTo(other);
}
return compC;
}
return super.compareTo(other);
}
}
The following is something that I tried. Not exactly sure this is the reason why findbugs gives the error.
See the following code with a hypothetical implementation of the compareTo method.
Comparing the same objects results in different outputs.
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints -2
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return (a + b) - (other.a + other.b);
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
public int compareTo(Impl other)
{
return super.compareTo(other) + (c - other.c);
}
}
Building on my hypothetical compareTo, following seems to be a good solution. You can try to have a method similar to getSum which gives the object instance a value.
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints 0
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return getSum() - other.getSum();
}
public int getSum()
{
return a + b;
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
#Override
public int getSum()
{
return super.getSum() + c;
}
}
As sp00m said, your Impl#compareTo(Impl) has a different signature than AbstractBase#compareTo(AbstractBase), so it's not overloading it.
The key point is in understanding why it doesn't work, even when you try to sort() comparing with another Impl, where the more specific signature do matches.
As you defined Comparable<AbstractBase>, you need to define how your
instances compareTo AbstractBase instances. And so you need to implement compareTo(AbstractBase).
You can think that, being Impl a subtype of AbstractBase, the more specific method would be used when a comparison between two Impls takes place. The problem is Java has static binding, and so the compiler defines at compile time which method would use for solving each method call. If what you were doing was sorting AbstractBases, then the compiler would use the compareTo(AbstractBase), that is the one AbstractBase's interface define when it implements the Comparable(AbstractBase) interface.
You can make Impl implement the Comparable<Impl> interface for using the compareTo(Impl) method, but that would only work if you explicitly sort things that are known to be Impls at compile time (ie, an Impl object or Collection<Impl>).
If you really want to apply a different comparison whenever your two objects are Impls, you should fall to some kind of double-dispatch in your Impl#compareTo(AbstractBase) like:
Impl >>>
int compareTo(AbstractBase other) {
return other.compareToImpl(this);
}
int compareToImpl(Impl other) {
// perform custom comparison between Impl's
}
AbstractBase >>>
int compareTo(AbstractBase other) {
// generic comparison
}
int compareToImpl(Impl other) {
// comparison between an AbstractBase and an Impl.
//Probably could just "return this.compareTo(other);", but check for loops :)
}
This requires you add some Impl information in your AbstractBase, which is not pretty, though, but solves the problem the more elegant way it could - using reflection for this is not elegant at all.
The Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) states: if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)
In your case, you are overriding the compareTo method from the Base class in a way that breaks the behaviour of the original method. This is probably why FindBugs has an issue with it.
If you want to be proper about it:
abstract class AbstractBase {
}
class Impl1 extends AbstractBase implements Comparable<Impl1> ...
class Impl2 extends AbstractBase implements Comparable<Impl2> ...
OR
even better, do not use the Comparable interface at all - use a Comparator at sort time instead.
However, in real life there are situations where you need to get around it (maybe you don't have access to the source of AbstractBase, or maybe your new class is just a POC). In these special cases, I would go with the "ugly" cast solution proposed by John.

Enforcing same generics type in a class using generics

The following example is taken from GenericsFAQ:
class Pair<X,Y> {
private X first;
private Y second;
public Pair(X a1, Y a2) {
first = a1;
second = a2;
}
public X getFirst() { return first; }
public Y getSecond() { return second; }
public void setFirst(X arg) { first = arg; }
public void setSecond(Y arg) { second = arg; }
}
Question: I wanted to enforce X and Y should be of same type. Example Pair<Integer,Integer> is correct but Pair<Integer, String> should not be accepted. Is it possible to achieve this through Generics?
Use
class Pair<X> {
And change all Y to X.
Did you consider something like this?
class LikePair<Z> extends Pair<Z,Z> {
public LikePair(Z a, Z b) {
super(a, b);
}
}
That way you get to keep the (potentially very useful) Pair class whilst also enforcing your 'likeness' constraint were needed.
As a matter of programming style I'd make Pair (and LikePair) immutable (final fields, no setters). Would also be good to implement equals(), hashCode(), toString(), etc.

Generic class - operator issue

I'm trying to do a generic class in Java.
Reading some guides, I found how to declare it and also how to call its function as well.
My class is a point and without methods it would be in this way:
class Pnt<type>{
protected type x, y;
protected int col;
}
Now, I'm trying to make an add method, but I am not able to do it.
What I tried is:
void add(type x_, type y_){
x += x_;
y += y_;
}
The IDE is yelling at me that += is undefined for type variables...
I know that in Java isn't possible to define a new operator like in C++, so I'm asking for an alternative way to add two type variables!
P.S. All the type that I will use would be doubles, floats and integers, that's why I'm trying to make a simple addiction.
When you say class Pnt<type>, ti means that type is an object and not a primitive data type like int or double. You can perform += operation on numeric primitive data types like int, float, etc and not on an object. In fact you can't perform the += operation with any generic object.
The objects of Integer, Float, etc. support the += operator, because they are wrapper classes and get unboxed to primitive types and autoboxed later. But the compiler has no way to confirm that the type will be an Integer or Float. Therefore, it generates a compile time error.
There are two problems: First, if it should be fast, you have to use primitives directly, and these are not supported as a parameter in generics. This effectively means you have to maintain three versions for Point separately.
If you want to use generics, you can use the corresponding class (like Integer), but there is still one problem: Their super type Number does not have an add method (let alone a + operator or +=).
So the only way I know is to implement your own numeric class hierarchy which supports an add method:
abstract class Numeric<T extends Number> {
public abstract T getValue();
public abstract Numeric<T> add(Numeric<T> other);
#Override
public String toString() {
return getValue().toString();
}
}
class MyInt extends Numeric<Integer> {
public final Integer value;
public MyInt(Integer _value) {
super();
this.value = _value;
}
#Override
public Integer getValue() {
return this.value;
}
#Override
public Numeric<Integer> add(Numeric<Integer> other) {
return new MyInt(this.value + other.getValue());
}
}
class MyDouble extends Numeric<Double> {
public final double value;
public MyDouble(Double _value) {
super();
this.value = _value;
}
#Override
public Double getValue() {
return this.value;
}
#Override
public Numeric<Double> add(Numeric<Double> other) {
return new MyDouble(this.value + other.getValue());
}
}
Based on this you can implement at least a generic point:
class NumericPoint<T extends Number> {
public final Numeric<T> x;
public final Numeric<T> y;
public NumericPoint(Numeric<T> _x, Numeric<T> _y) {
super();
this.x = _x;
this.y = _y;
}
public NumericPoint<T> add(NumericPoint<T> other) {
return new NumericPoint<T>(this.x.add(other.x), this.y.add(other.y));
}
#Override
public String toString() {
return "(" + this.x + "/" + this.y + ")";
}
}
to be used with
NumericPoint<Integer> ip1 =
new NumericPoint<Integer>(new MyInt(1), new MyInt(2));
NumericPoint<Integer> ip2 =
new NumericPoint<Integer>(new MyInt(3), new MyInt(4));
NumericPoint<Integer> ip = ip1.add(ip2);
System.out.println(ip);
NumericPoint<Double> dp1 =
new NumericPoint<Double>(new MyDouble(1.1), new MyDouble(2.1));
NumericPoint<Double> dp2 =
new NumericPoint<Double>(new MyDouble(3.1), new MyDouble(4.1));
NumericPoint<Double> dp = dp1.add(dp2);
System.out.println(dp);
I have modified your example: The numerics and the points are immutable. It's implemented like BigDecimal for example. So the add method belongs to the generic class, and it returns a new instance.

Generic type extending Number, calculations

I've made an interface of math operation with one method, calculate, taking various number of arguments
public interface MathOperation {
public <T extends Number> T calculate(T... args);
}
There's also simple implementation of this class, which does not work:
private class Sum implements MathOperation {
#Override
public <T extends Number> T calculate(T... args) {
return args[0] + args[1];
}
}
The problem is:
bad operand types for binary operator '+'
first type: T
second type: T
where T is a type-variable:
T extends Number declared in method <T>calculate(T...)
What I'm trying to achieve is a simple class, taking for example two Doubles and returning Double as well.
Is there possibility to achieve this?
+ cannot be applied to types that extend Number. new Integer(5) + new Integer(5) works because of autoboxing. You will have to look at the runtime type of args and do the operation accordingly.
Something on the lines of:
private class Sum implements MathOperation {
#Override
public <T extends Number> T calculate(Class<T> clazz, T... args) {
if (clazz.equals(Integer.class))
{
return Integer.class.cast(args[0]) + Integer.class.cast(args[1]);
} else (....)
}
}
For Addition we can use doubleValue() method of Number class. To return the same type value, the idea is to use a Function or Supplier or a Factory to create instances of the type T.
class MathOperation<T extends Number> {
public double add(T a, T b) {
double d = a.doubleValue() + b.doubleValue();
return d;
}
public T add(T a, T b, Function<Double,T> function) {
double d = a.doubleValue() + b.doubleValue();
return function.apply(d);
}
}
You can test the runtime type as shown in the other answers. Or you can try a different design: Create an abstract class that works as a factory:
interface MathContext<T extends Number> {
...
T valueOf(double d);
T valueOf(int i);
T add (T... args);
}
And concrete classes for the types that you want to use:
DoubleContext implements MathContext<Double> {
...
Double valueOf(int i) {
return i;
}
Double valueOf(double d) {
return d;
}
Double add(Double... args) {
Double res = 0;
for (Double arg: args) {
res += arg;
}
return res;
}
}
Now you could implement your MathOperation using that class. However, it's not really needed any more.

Categories