2015-02-26 6 views
1

Проблема заключается в том, что следующий код не может скомпилировать, если общая сигнатура состоит из нескольких ?, которые являются тем же самым типом.Java Generic Advanced Usage

import java.util.Map; 
import java.util.HashMap; 
import java.util.function.Function; 

public class Test { 

    private static <T> T findSelfReference(Map<T, T> map) { 
     for (Map.Entry<T, T> entry : map.entrySet()) { 
      if (entry.getKey() == entry.getValue()) { 
       return entry.getKey(); 
      } 
     } 
     return null; 
    } 

    private static <T> T findSelfReference2(Map<T, T> map) { 
     for (T key : map.keySet()) { 
      if (map.get(key) == key) { 
       return key; 
      } 
     } 
     return null; 
    } 

    // Question: How to write the method signature that can ensure compile-time type safety? Both the signatures fail to compile. 

    // private static <T> String fun(Function<Map<T, T>, T> finder) { 
    private static String fun(Function<Map<?, ?>, ?> finder) { 
     Map<Integer, Integer> map1 = new HashMap<>(); 
     // some processing to map1 
     Integer n = finder.apply(map1); // usage here, compile-time type checking wanted 

     Map<String, String> map2 = new HashMap<>(); 
     // other processing to map2 depending on n 
     return finder.apply(finder, map2); // another usage 
    } 

    public static void main(String[] args) { 
     // Please don't change into helper class... 
     System.out.println(fun(Test::findSelfReference)); 
     System.out.println(fun(Test::findSelfReference2)); 
    } 
} 

В fun, внутри каждого вызова finder.apply() типа T фиксируется. Но среди разных вызовов они используют разные типы. Я попробовал подстановочный знак (ref: here), но не повезло.

Я не хочу приводить результат в объект, в котором проверка должна выполняться во время выполнения. Вся проверка типов должна выполняться во время компиляции.

Возможно ли использование классов вспомогательных функций O (n), где n - число встроенных функций?

ответ

4

Проблема заключается в том, что вы хотите, чтобы метод finderapply был общим.

Решение состоит в том, чтобы определить собственный функциональный интерфейс с помощью общего метода.

@FunctionalInterface 
interface Finder { 
    <T> T apply(Map<T, T> map); 
} 

private static String fun(Finder finder) { 
    // same body 
} 

Если это считается «вспомогательным классом», то я не знаю, что вам сказать. Вы пытаетесь заколоть квадратную привязку в круглое отверстие. Function#apply не является общим методом, поэтому вы не можете сделать это с помощью Function.

2

Что именно вы пытаетесь достичь своим методом fun?

Вы передаете ему Function либо с групповыми символами или с параметром типа T, а затем вы хотите, чтобы применить его на два различных конкретных типов (Integer и String).

Это не будет работать:

Map<Integer, Integer> map1 = new HashMap<>(); 
// ... 
Integer n = finder.apply(map1); 

Потому что здесь вы ожидаете finder принять тип Integer, но вы указали в декларации fun, что либо тип неизвестен (если вы объявляете его с ?) или с некоторым неограниченным типом T (если вы объявите его с параметром типа T). Но если вы собираетесь применить его к Integer, вам нужен Function<Map<Integer, Integer>>, а не Function<Map<?, ?>, ?> или Function<Map<T, T>, T> для некоторого неограниченного типа T.

Вы можете написать его вот так - но метод fun сам более или менее бесполезен.

private static <T> T fun(Map<T, T> map, Function<Map<T, T>, T> finder) { 
    return finder.apply(map); 
} 

public static void main(String[] args) { 
    Map<Integer, Integer> map1 = new HashMap<>(); 
    map1.put(1, 2); 
    map1.put(3, 4); 
    map1.put(5, 7); 
    map1.put(2, 2); 
    map1.put(8, 8); 
    Integer n = fun(map1, Test::findSelfReference); 

    String a = String.valueOf(n + 1); 
    Map<String, String> map2 = new HashMap<>(); 
    map2.put("1", "2"); 
    map2.put("3", "4"); 
    map2.put("5", "7"); 
    map2.put("3", a); 
    String s = fun(map2, Test::findSelfReference2); 

    System.out.println(n); 
    System.out.println(s); 
} 
2

Ну вы можете сделать что-то вроде этого:

private static <T> T fun(Function<Map<T, T>, T> finder, Map<T, T> map) { 
    return finder.apply(map); 
} 

private static Map<String, String> getStringMap(Integer n) { 
    String a = String.valueOf(n + 1); 
    Map<String, String> map2 = new HashMap<>(); 
    map2.put("1", "2"); 
    map2.put("3", "4"); 
    map2.put("5", "7"); 
    map2.put("3", a); 
    return map2; 
} 

private static Map<Integer, Integer> getIntMap() { 
    Map<Integer, Integer> map1 = new HashMap<>(); 
    map1.put(1, 2); 
    map1.put(3, 4); 
    map1.put(5, 7); 
    map1.put(2, 2); 
    map1.put(8, 8); 
    return map1; 
} 

public static void main(String[] args) { 
    fun(Test::findSelfReference, getIntMap()); 
    fun(Test::findSelfReference, getStringMap(1)); 
} 

Я не вижу цель fun метода, когда вы всегда можете сделать это:

Function<Map<Integer, Integer>, Integer> m = Test::findSelfReference; 
m.apply(getIntMap());