Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Use Generics in a map of Comparator to avoid warnings

Tags:

java

generics

I want to create a map of comparators as following, this map will be used to provide the comparator for each kind of class.

How can I replace the Generic ? in the declaration of my map to be sure that I have always the same Class type in key and value of my map (comparators )?

I want also to reduce the number of warnings

 private static final Map<Class<?>, Comparator<?>> comparators = new HashMap<>();

static {
    comparators.put(Identifiable.class, new Comparator<Identifiable>() {
        @Override
        public int compare(Identifiable o1, Identifiable o2) {
            return o1.getId().compareTo(o2.getId());
        }
    });
    comparators.put(MyClass.class, new Comparator<MyClass>() {
        @Override
        public int compare(EIntersection o1, EIntersection o2) {
            return o1.getRef().compareTo(o2.getRef());
        }
    });
    ...
}
like image 812
Aguid Avatar asked Oct 25 '17 08:10

Aguid


1 Answers

To reduce the amount of warnings you can put a wrapper around your comparator map (like in this answer).

With this solution you have to cast the type only once in your wrapper class (and get only one warning). When calling get on your wrapper class you don't get a warning and don't have to cast the value.

Example:

private static final Map<Class<?>, Comparator<?>> oldComparatorsMap = new HashMap<>();  
private static final Map<Class<?>, Comparator<?>> newComparatorsMap = new MyTypeSafeMap();

static {
    oldComparatorsMap.put(String.class, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }
    });
    oldComparatorsMap.put(Integer.class, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });
    newComparatorsMap.put(String.class, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }
    });
    newComparatorsMap.put(Integer.class, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });
}

public static void main(String[] args) {

  Comparator<String> c1 = (Comparator<String>)oldComparatorsMap.get(String.class); // Warning for every call on get with a cast

  Comparator<String> c2 = newComparatorsMap.get(String.class); // No warning here and no cast

}

static class MyTypeSafeMap {
  private static final Map<Class<?>, Comparator<?>> innerComparatorsMap = new HashMap<>();

  public <T> void put(Class<T> key, Comparator<T> value) {
    innerComparatorsMap .put(key, value); 
  }

  public <T> Comparator<T> get(Class<T> key) {
    return (Comparator<T>) innerComparatorsMap .get(key); // Only one warning here
    // we know it's safe, but the compiler can't prove it
  }
}
like image 124
Benjamin Schüller Avatar answered Sep 19 '22 00:09

Benjamin Schüller