Подтвердить что ты не робот

Как проверить, реализует ли общий тип определенный тип общего интерфейса в java?

Возможный дубликат:
Общий тип локальной переменной во время выполнения

Я новичок в Java-дженериках и, исходя из .NET-мира, я привык к тому, что могу написать такой метод:

public void genericMethod<T>(T genericObject)
{
    if (genericObject is IList<String>)
    {
        //Do something...
    }            
}

Метод принимает объект родового типа и проверяет, реализует ли этот объект конкретную версию общего интерфейса IList<>, в данном случае IList<String>.

Теперь, в Java, я могу это сделать:

public <T> void genericMethod(T genericObject)
{
    if (genericObject instanceof Set<?>)
    {
       //Do something...
    }
}

НО

Java не позволяет мне делать if (genericObject instanceof Set<String>)

Из того, что я знаю, из-за стирания типа, обычно в Java, это будет заботиться об объекте класса, и мы будем делать что-то вроде следующего:

public <T> void genericMethod(T genericObject)
{
    Class<OurTestingType> testClass = OurTestingType.class;
    if (genericObject.getClass() == testClass)
    {
       //Do something...
    }
}

но поскольку тип, который я проверяю, является общим интерфейсом, вы не можете этого сделать:

Class<Set<String>> testClass = Set<String>.class

Итак, как на Java проверять, реализует ли общий объект определенный тип Set<String>?

4b9b3361

Ответ 1

Java реализует стирание, поэтому нет способа указать во время выполнения, если genericObject является экземпляром Set<String> или нет. Единственный способ гарантировать это - использовать границы ваших дженериков или проверить все элементы в наборе.

Использование проверки границ:

public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
    // Do something with tSet here
}

С итерацией и проверкой типов:

public <T> void genericMethod(T t) {
    Set<String> strs = new HashSet<String>();
    Set<?> tAsSet;
    if (t instanceof Set<?>) {
        tAsSet = (Set<?>) t;
        for (Object obj : tAsSet) {
            if (obj instanceof String) {
                strs.add((String) obj);
            }
        }
        // Do something with strs here
    } else {
        // Throw an exception or log a warning or something.
    }
}

Изменить: В соответствии с комментарием Mark Peters ниже, у Guava также есть методы, которые делают это для вас, если вы можете добавить его в свой проект:

public <T> void genericMethod(T t) {
    if (t instanceof Set<?>) {
        Set<?> set = (Set<?>) t;
        if (Iterables.all(set, Predicates.instanceOf(String.class))) {
            Set<String> strs = (Set<String>) set;
            // Do something with strs here
        }
    }
}

Утверждение Iterables.all(set, Predicates.instanceOf(String.class)) по существу совпадает с set instanceof Set<String>.

Ответ 2

У вас нет такой опции в Java, к сожалению. В Java не существует времени выполнения между List<String> и a List<Integer>. Это компилятор, который гарантирует, что вы никогда не add() a Integer до List<String>. Даже это принудительное исполнение компилятора не является строгим, поэтому вы можете "юридически" совершать такие мерзости с помощью непроверенных бросков....

В общем, для (почти) любого вопроса идентификации типа времени выполнения вы должны взять List<String> за то, что он на самом деле: просто raw List. Это называется стиранием типа.

Тем не менее, ничто не мешает вам проверять содержимое List для своих типов:

public boolean isListOf(List<?> list, Class<?> c) {
    for (Object o : list) {
        if (!c.isInstance(o)) return false;
    }

    return true;
}

Чтобы использовать этот метод:

    // ...
    if (genericObject instanceof List<?>) {
        if (isListOf((List<?>) genericObject, String.class)) {
            @SuppressWarnings("unchecked")
            List<String> strings = (List<String>) genericObject;
        }
    }

Интересное наблюдение: если List пуст, метод возвращает true для всех заданных типов. На самом деле не существует разницы во времени между пустым List<String> и пустым List<Integer>.