Как определить, является ли параметр правильным типом в Generics - PullRequest
2 голосов
/ 11 августа 2011

Я пишу реализацию карты со следующей обязательной сигнатурой метода.

Мне нужно убедиться, что значение имеет правильный тип.Я думал, что эта проверка вызовет исключение, если два типа не будут равны, но это не так.Есть ли способ проверить это?

public boolean containsValue(Object value) {

    try {
        V temp = (V) value;
    } catch (Exception e) {
        throw new ClassCastException("Value is not of correct type");
    }
    ...

Ответы [ 3 ]

4 голосов
/ 11 августа 2011

Из-за стирания типа вы не можете использовать стандартный оператор instanceof. Канонический подход состоит в том, чтобы передать сам класс или его экземпляр куда-нибудь.

Затем вы можете использовать отражение, чтобы проверить тип экземпляра:

private Class<V> clazz; // somehow this gets set

public boolean containsValue(Object value){

    if(clazz.isInstance(value)){
        // safe:
        V temp = (V) value;
    }

Вы также можете проверить дерево наследования, используя Class.isAssignableFrom:

    if(clazz.isAssignableFrom(value.getClass())){
        // safe:
        V temp = (V) value;
    }

Обратите внимание, что это гарантирует, что это типобезопасное приведение, но не требует, чтобы типы были одинаковыми, например, V может быть Number, но value может иметь тип Integer.

Что касается получения clazz, вы можете применить его в своем конструкторе:

public class MyMap<K,V>{

    private final Class<V> clazz;

    public MyMap(Class<V> clazz){
        this.clazz = clazz;
    }

...

И ваш инициализатор для класса может выглядеть примерно так:

 MyMap<Integer,String> foo = new MyMap<Integer,String>(String.class);

Обычно я поддерживаю передачу типа в конструкторе для такого рода вещей и помечаю поле как final, так как семантика и ограничения не позволят вам изменить это поле, что является разумным, поскольку экземпляр обычно полагается на гарантии времени компиляции, которые предполагают, что параметры типа не изменены.

1 голос
/ 11 августа 2011

Зачем вам нужно, чтобы значение было типа V? Интерфейс карты был определен таким образом не случайно.

Вы должны принять значение, являющееся объектом, а затем вызвать value.equals (someOtherValueInYourMap).

Например:

public boolean containsValue(Object value) {
    V v1 = ...;
    V v2 = ...;
    V v3 = ...;
    return value.equals(v1) || value.equals(v2) || value.equals(v3);
}

при условии, что на вашей карте есть три значения v1, v2, v3. Неважно, какой тип v1, v2, v3 и значение аргумента, потому что метод equals определен в Object, и вы всегда можете вызвать его для сравнения объектов. Если значение действительно имеет другой тип, который нельзя сравнить с экземплярами V, то его метод equals вызовет исключение ClassCastException и, если вы его не отловите, оно будет распространено дальше. Не беспокойся об этом.

0 голосов
/ 11 августа 2011

Причина, по которой оно не выдает исключение, состоит в том, что карта не может содержать объект другого типа, поэтому ложный ответ является правильным.

Что касается вашего вопроса, то благодаря стиранию типов нет, нет никакого способа увидеть, является ли переданный объект тем же, что и универсальный тип.

...