1

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?

Tom Wellbrock
  • 2,982
  • 1
  • 13
  • 21

4 Answers4

3

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".

Community
  • 1
  • 1
Tom
  • 16,842
  • 17
  • 45
  • 54
1

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.

Jan Chimiak
  • 736
  • 7
  • 20
1

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);
}
Alex - GlassEditor.com
  • 14,957
  • 5
  • 49
  • 49
  • I like the idea with the comparator. The idea was to do a simple method for simple purposes but i can implement your extended method as well :)...sadly we work with java 7, I hope we recive the update soon – Tom Wellbrock Feb 17 '16 at 13:45
  • The java 8 sample does not work for me he cannot resolve the method – Tom Wellbrock Feb 17 '16 at 13:59
  • @TomWellbrock I have compiled it with eclipse and javac 1.8.0_51, both of these methods worked each time, but calling the comparator version gave me an error with javac. You may need to change the name of one of the methods. Which compiler version are you using? – Alex - GlassEditor.com Feb 17 '16 at 14:17
  • Im using javac 1.8.0_74. IntelliJ dosent even want to build. It says: "Cannot resolve method isBetween(,T,T,T[])" – Tom Wellbrock Feb 17 '16 at 14:30
  • heres the compile error: Error:(14, 12) java: method isBetween in class de.popken.Main cannot be applied to given types; required: T,T,T[] found: T::compareTo,T,T,T[] reason: inferred type does not conform to upper bound(s) inferred: java.lang.Object upper bound(s): java.lang.Comparable super java.lang.Object>,T – Tom Wellbrock Feb 17 '16 at 14:37
  • @TomWellbrock I've tried it with 1.8.0_74 and IntelliJ now, I see the same error but when I try to compile it it says "compilation completed succesfully" and it runs fine, so I geuss it could be a bug in IntelliJ – Alex - GlassEditor.com Feb 17 '16 at 16:09
  • 1
    The error also goes away when I change one of the method names, so you may want to just do that. – Alex - GlassEditor.com Feb 17 '16 at 16:15
  • Changed the name and it works like a miracle thanks ! – Tom Wellbrock Feb 18 '16 at 07:54
0

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.

TouDick
  • 1,262
  • 12
  • 18