2016-02-29 1 views
0

В настоящее время я работаю с Generics, и я не настолько опытен. Я получил эту проблему:Общий подстановочный вызов не будет компилироваться

Я сделал этот класс, чтобы сравнить 2 значения, реализующие Comparable и сделать некоторые другие вещи, скажем приращение compareTo() значения:

public final class Type<T extends Comparable<T>> { 
    public int compare(T val1, T val2) { 
     int compTemp = val1.compareTo(val2); 
     compTemp++; 
     // do stuff 
     return compTemp; 
    } 
} 

Теперь я создаю несколько экземпляров из Type:

Type<Integer> t1 = new Type<Integer>(); 
Type<String> t2 = new Type<String>(); 

А теперь я хочу, чтобы поместить их в Map. Как я не могу предсказать общий тип Type я использую wilcard ?:

Map<String, Type<?>> map = Maps.newHashMap(); 

map.put("t1", t1); 
map.put("t2", t2); 

Если я хочу, чтобы взывать Type.compare() это работает t1, но не для map.get(t1):

t1.compare(1,1); // compiles fine 
map.get("t1").compare(1, 1); // does not compile 

Последнее вызывает ошибку компиляции:

The method compare(capture#3-of ?, capture#3-of ?) in the type Type is not applicable for the arguments (int, int)

Я знаю, что это из-за моего wildcard, но я точно не знаю, почему и как это исправить. Единственное «исправление», которое я вижу, это использовать необработанные типы для Карты, однако это покажет несколько предупреждений. Я полагаю, что сейчас у меня есть огромное непонимание параметра wilcard.

Я ценю каждый ответ!

+0

'.get (t1)'? Вашими ключами к карте являются 'String's; ваш 't1' не является строкой. – fge

+0

typo, но не меняет факт, что он не работает – Don

+3

Подстановочный знак означает, что вам не нужен фактический тип, который он представляет. Но ясно, что вам все равно, вот в чем проблема. Подумайте о том, что произойдет, если вы передадите мне эту карту, и я тайно сделал «map.put» («t1», новый тип ()); '? Как мог компилятор обнаружить это? – biziclop

ответ

2

Вы используете wildcard <?> на своей карте, так как ваша Карта не знает точного типа. Для Карты все они хранятся как Type<Object>. Одна вещь, которую вы можете сделать здесь некоторый тип UnSafe Casting

((Type<Integer>)map.get("t1")).compare(1, 1); // does compile but is type unsafe 

Другим решением было бы сохранить общий класс при создании Type объектов в конструкторе. Затем вы можете сделать какое-то безопасное литье в вашем методе compare, используя другой метод generic Type. Смотрите этот Type класс

public final class Type<T extends Comparable<T>> { 
    Class<T> type; 
    Type(Class<T> _type){ 
     type = _type; 
    } 

    public <E extends Comparable<E>> int compare(E val1, E val2) { 
     T v1 = type.cast(val1); 
     T v2 = type.cast(val2); 

     int compTemp = v1.compareTo(v2); 
     compTemp++; 
     // do stuff 
     return compTemp; 
    } 
} 

Теперь вы можете сделать это:

Type<Integer> t1 = new Type<Integer>(Integer.class); 
    Type<String> t2 = new Type<String>(String.class); 

    Map<String, Type<?>> map = new HashMap<>(); 

    map.put("t1", t1); 
    map.put("t2", t2); 

    map.get("t1").compare(1, 1); // compiles fine 
    map.get("t2").compare("one", "one"); // compiles fine 
+0

Это именно то, чего я не хочу, поскольку я могу повторять свою Карту и не знаю общий тип. – Don

+0

Попробуйте ((Тип) map.get ("t1")). Сравните (1, 1), который будет компилироваться, но не введите безопасность –

+1

@ Если вы не знаете тип во время компиляции, то как бы вы параметры подачи, то есть, как бы вы знали тип параметров? Предположим, вы назвали 'map.get (" t2 "). Compare (1,1)', который не должен работать. – Thomas

2

Я предполагаю, что вы хотите сделать что-то подобное во время выполнения:

String key = getKeyFromParams(p1, p2); 
map.get(key).compare(p1, p2); 

Так как компилятор не знает общий тип значения, которое он возвращает для ключа, вы не можете просто вызвать compare() таким образом.

Вы могли бы, однако, перестроить свой код немного, чтобы сделать его работу (по-прежнему с предупреждениями и о необходимости заботиться, чтобы не сломать что-нибудь):

  1. Сделайте ваш сравнить() метод принимает Comparable параметры
  2. Pass класс универсального типа конструктора и использовать его, чтобы проверить параметры сравнения()
    • если типы параметров хорошо сравнить их
    • , если они этого не делают соответствует классу типа сгенерирует исключение
  3. использовать класс типа в качестве ключа карты и использовать его для поиска

Пример:

public final class Type<T extends Comparable<T>> { 
    private final Class<T> typeClass; 

    public Type(Class<T> typeClass) { 
    this.typeClass = typeClass; 
    } 

    public int compare(Comparable val1, Comparable val2) { 
    if(!(typeClass.isInstance(val1) && typeClass.isInstance(val2))) { 
     throw new IllegalArgumentException("message"); 
    } 

    int compTemp = val1.compareTo(val2); 
    compTemp++; 
    // do stuff 
    return compTemp; 
    } 

    //getter for the typeClass 
} 

Обертка для карты:

class TypeComparator { 
    Map<Class<?>, Type<?>> map = new HashMap<Class<?>, Test.Type<?>>(); 

    public void addType(Type<?> type) { 
    map.put(type.getTypeClass(), type); 
    } 

    public <T extends Comparable<T>> void compare(T p1, T p2) { 
    map.get(p1.getClass()).compare(p1, p2); 
    } 
} 

И, наконец, назовите это так:

//add some types 
TypeComparator comp = new TypeComparator(); 
comp.addType(new Type<Integer>(Integer.class)); 
comp.addType(new Type<String>(String.class)); 

//compare ints  
comp.compare(1, 1); 

//compare Strings 
comp.compare("1", "1"); 

//won't compile 
comp.compare("1", 1); 

Некоторые заключительные мысли:

  • Вы должны обращаться со случаями, где нет типа на карте (например, если в этом примере были указаны параметры Double)
  • Вы по-прежнему будете получать предупреждения, но если вы хорошо спрячете их в реализации и будьте осторожны, чтобы не применять приведения к неправильным типам, вы должны быть в порядке.
+0

Я ценю ваш ответ, но это то же самое, что и ArcticLord, если я что-то не пропущу. – Don

+0

@ Да, да, это в основном то же самое. К сожалению, здесь не так много вариантов. Если вы немного уточните, что вы в конечном итоге пытаетесь сделать, может быть лучшее решение. – Thomas

+0

Я окончательно должен сделать некоторые редизайны. Но вы, ребята, дали мне хорошее решение и обнаружили мою ошибку дизайна. – Don

Смежные вопросы