Reputation: 3042
to extend my departments private Libary I tried to implement a isBetween method. This method is generic and expects a Comparable
type T
for its minimum value, its maximum value, and for an varargs values. Its purpose is to examine if all values are within the specified range.
public static void main(String[] args) {
System.out.print(isBetween(1, 100, 200, 100, 1, 2));
}
@SafeVarargs
public static <T extends Comparable> boolean isBetween(T minimum, T maximum, T... values) {
for (T value : values) {
if (!(value.compareTo(minimum) >= 0 && value.compareTo(maximum) <= 0))
return false;
}
return true;
}
This works just fine. But the type T can contain any Comparable objects. A method call:
System.out.print(isBetween(1, 100, 200, 100, "How awkward", 2));
would also be accepted at compile-time. This is not typesafe at all and can't be accepted.
Two solutions came into my mind:
1. call the method like the following
System.out.print(Class.<Integer>isBetween(1, 100, 200, 100, 1, 2));
2. make one of my method parameters of type U
and extend type T
to type U
Both "solutions" do not seem to be very elegant. The first one requires to write additional code before you call the method and the second one seems like a hack.
Are there any more elegant ways to solve my problem?
Upvotes: 1
Views: 145
Reputation: 15557
Another way you could do this is to pass in a Comparator
instead of requiring T
to be Comparable
:
public static <T> boolean isBetween(Comparator<? super T> comparator,
T minimum, T maximum, T... values) {
for (T value : values) {
if (!(comparator.compare(value, minimum) >= 0 &&
comparator.compare(value, maximum) <= 0)){
return false;
}
}
return true;
}
This allows you compare the objects however you want, and they don't need to be Comparable
.
You could also provide a similar method to the other answers to skip writing the Comparator
when T
is Comparable
:
public static <T extends Comparable<? super T>> boolean isBetween(T minimum,
T maximum, T... values) {
return isBetween(T::compareTo, minimum, maximum, values);
}
Upvotes: 1
Reputation: 17597
Well, this can be easily fixed by avoiding raw types.
Let's look at your code:
public static <T extends Comparable> boolean isBetween(T minimum, T maximum, T... values) {
// ...
}
T
is a nice and generic, but Comparable
is a raw type. You want Comparable<T>
, so your method should look like this:
public static <T extends Comparable<T>> boolean isBetween(T minimum, T maximum, T... values) {
// ...
}
and then isBetween(1, 100, 200, 100, "How awkward", 2)
won't be possible anymore.
The isBetween
method allows "equal" argument types if they have a equal super class which implements Comparable
.
For example using java.util.Date
and java.sql.Date
(user fge had this good idea):
isBetween(new java.util.Date(1L), new java.util.Date(4L), new java.sql.Date(5L))
This will result in "false", because the third argument is "greater", than the upper bound. And java.sql.Date
is accepted, because it extends java.util.Date
and will infer T
with it as the "common parent class which implements Comparable
".
Upvotes: 3
Reputation: 1322
Use Comparable < T > instead of raw Comparable. In this way, you will ensure that all instances of type T are of the same type and it will fail on compile.
Upvotes: 0
Reputation: 746
Make it <T extends Comparable<T>>
:
@SafeVarargs
public static <T extends Comparable<T>> boolean isBetween(T minimum, T maximum, T... values) {
for (T value : values) {
if (!(value.compareTo(minimum) >= 0 && value.compareTo(maximum) <= 0))
return false;
}
return true;
}
this will fail at compile.
Upvotes: 1