Как проверить, реализует ли общий тип определенный тип общего интерфейса в 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>
?
Ответы
Ответ 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>
.