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();
Related
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);
Is it ugly design that I overload a method depending on whether the argument is subtype or supertype?
I intend to implent a super class A and a subclass B, and the objects can compare with each other.
compareTo is overloaded twice both in class A and class B, and the code seems a little messy.
It feels like ugly design. I'm not sure if there is a more elegant approach.
class A implements Comparable<A> {
private Integer x;
public A(Integer i) {
x = i;
}
public Integer getX() {
return x;
}
public int compareTo(A other) {
return x.compareTo(other.getX());
}
public int compareTo(B other) {
return x.compareTo(other.getX() + other.getY());
}
}
class B extends A {
private Integer y;
public B(Integer a, Integer b) {
super(a);
y = b;
}
public Integer getY() {
return y;
}
#Override
public int compareTo(A other) {
return Integer.compare(this.getX() + y, other.getX());
}
#Override
public int compareTo(B other) {
return Integer.compare(this.getX() + y, other.getX() + other.getY());
}
}
Yes it is a bad design. Something like this would be better.
class A implements Comparable<A> {
private Integer x;
public A(Integer i) {
x = i;
}
public Integer getX() {
return getX();
}
protected Integer compareValue() {
return getX();
}
#Override
public int compareTo(A other) {
return compareValue().compareTo(other.compareValue());
}
}
class B extends A {
Integer y;
public B(Integer a, Integer b) {
super(a);
y = b;
}
public Integer getY() {
return y;
}
#Override
protected Integer compareValue() {
return getX() + getY();
}
}
It's bad design, yes. Specifically, it violates transitivity. From the Comparable Javadocs:
The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
When you introduce inheritance, you lose transitivity. That's because your A.compareTo(B) method doesn't actually override anything, and so if you passed a List<A> to Collections.sort() which contained both A and B, it would end up using the A.compareTo(A) method for the A instances and the B.compareTo(A) method for the B instances. As you've written them, they are not transitive, and therefore would result in unpredictable ordering since reversing the comparison can change the sort order.
I wrote a similar response w.r.t. Object.equals and why you can't overload it a while back, which you can find here.
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.
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.
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.