Type-safely create instance of Function to be passed to Comparator.comparing()

Ondra :

Assume I have a method with the following signature:

<T, U extends Comparable<? super U>> Comparator<T> method(Map<String, Function<? super T, ? extends U>> comparatorFunctionMap)

The method accepts a map of functions (with string keys) and creates a Comparator<T> as a result (it isn't important how). Map values are instances of Function<? super T, ? extends U>, so that they can be directly passed to Comparator.comparing().

How do I populate this map in a type-safe way? Say I have a class Person with attributes name and age (and getters for them).

When I do the following:

Map<String, Function<? super Person, ? extends Comparable>> map1 = new HashMap<>();
map1.put("name", Person::getName);
method(map1);

I get a warning on lines 1 and 3. If I try this instead, for example:

Map<String, Function<? super Person, ? extends Comparable<?>>> map2 = new HashMap<>();
map2.put("name", Person::getName);
method(map2);

The third line is a compile error.

Is there a way to do this type-safely?

assylias :

If you want to be able to add both Person::getName and Person::getAge in the map, you won't be able to use the method signature you propose because there is no U that is a supertype of both String, Integer and Comparable.

Essentially, your map is a Map<String, Function<T, Comparable<?>> since the comparables are not related to each other type-wise.

I don't think you can get around it without using raw types, which could look like below. Note that you still have type safety (you must pass a Comparable to the map).

static void m() {
  Map<String, Function<Person, Comparable<?>>> map = new HashMap<>();
  map.put("name", Person::getName);
  map.put("age", Person::getAge);
  Comparator<Person> c = method(map);
}


@SuppressWarnings(value = {"unchecked", "rawtypes"})
static <T> Comparator<T> method(String name, Map<String, Function<T, Comparable<?>>> comparatorFunctionMap) {
  Function f = (Function) comparatorFunctionMap.get("age");
  Comparator<T> c = Comparator.comparing(f);
  return c;
}

The only (I think) limitation of this is that technically, one could pass a weird class say Weird implements Comparable<String> that would probably cause a runtime error.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=462687&siteId=1