Типы isAssignable и isSubtype недопонимание
При написании процессора аннотаций с использованием API Java 6 я столкнулся с необходимостью обрабатывать все карты определенным образом, но я явно не понимаю, для чего предназначен API или как его вызывать. Вот код, который делает меня несчастным:
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.annotation.processing.ProcessingEnvironment;
...
public String doThing(Element el, ProcessingEnvironment processingEnv) {
// Utilities from the ProcessingEnvironment
Types typeUtils = processingEnv.getTypeUtils();
Elements elementUtils = processingEnv.getElementUtils();
// The type of the element I'm handling
TypeMirror elType = el.asType();
// Compare the element's type to Map
TypeMirror mapType = elementUtils.getTypeElement("java.util.Map").asType();
System.out.println(elType + " > " + mapType + " = " + typeUtils.isSubtype(elType, mapType));
System.out.println(mapType + " > " + elType + " = " + typeUtils.isSubtype(mapType, elType));
System.out.println(elType + " > " + mapType + " = " + typeUtils.isAssignable(elType, mapType));
System.out.println(mapType + " > " + elType + " = " + typeUtils.isAssignable(mapType, elType));
// Compare the element's type to HashMap
TypeMirror hashmapType = elementUtils.getTypeElement("java.util.HashMap").asType();
System.out.println(elType + " > " + hashmapType + " = " + typeUtils.isSubtype(elType, hashmapType));
System.out.println(hashmapType + " > " + elType + " = " + typeUtils.isSubtype(hashmapType, elType));
System.out.println(elType + " > " + hashmapType + " = " + typeUtils.isAssignable(elType, hashmapType));
System.out.println(hashmapType + " > " + elType + " = " + typeUtils.isAssignable(hashmapType, elType));
// Compare the element's type to Object
TypeMirror objectType = elementUtils.getTypeElement("java.lang.Object").asType();
System.out.println(elType + " > " + objectType + " = " + typeUtils.isSubtype(elType, objectType));
System.out.println(objectType + " > " + elType + " = " + typeUtils.isSubtype(objectType, elType));
System.out.println(elType + " > " + objectType + " = " + typeUtils.isAssignable(elType, objectType));
System.out.println(objectType + " > " + elType + " = " + typeUtils.isAssignable(objectType, elType));
}
Учитывая это, вот результат этого:
java.util.HashMap<K,V> > java.util.Map<K,V> = false
java.util.Map<K,V> > java.util.HashMap<K,V> = false
java.util.HashMap<K,V> > java.util.Map<K,V> = false
java.util.Map<K,V> > java.util.HashMap<K,V> = false
java.util.HashMap<K,V> > java.util.HashMap<K,V> = true
java.util.HashMap<K,V> > java.util.HashMap<K,V> = true
java.util.HashMap<K,V> > java.util.HashMap<K,V> = true
java.util.HashMap<K,V> > java.util.HashMap<K,V> = true
java.util.HashMap<K,V> > java.lang.Object = true
java.lang.Object > java.util.HashMap<K,V> = false
java.util.HashMap<K,V> > java.lang.Object = true
java.lang.Object > java.util.HashMap<K,V> = false
Это имеет смысл для меня, за исключением первого блока, где я ожидал, что элемент HashMap будет назначаться для Map, и я ожидал, что HashMap будет подтипом Map.
Что мне здесь не хватает?
2 ответа
Я подозреваю, что это из-за переменных типа. HashMap<String, String>
присваивается Map<String, String>
но без конкретной реализации переменных типа вы не можете быть уверены, что произвольный HashMap<A,B>
присваивается Map<X,Y>
,
Если вы создаете переменные с помощью символов подстановки, вы должны получить ожидаемый результат
DeclaredType wildcardMap = typeUtils.getDeclaredType(
elementUtils.getTypeElement("java.util.Map"),
typeUtils.getWildcardType(null, null),
typeUtils.getWildcardType(null, null));
Это даст вам тип зеркала для Map<?,?>
, который все HashMap
экземпляры присваиваются.
Обновление (март 2016 г.): на основании комментария @user1643723, похоже, types.erasure(TypeMirror)
библиотечная функция, о которой я не знал в 2012 году.
Основываясь на ответе Яна, я теперь использую следующий метод для сопоставления базовых типов, как я описал для карты в вопросе.
TypeElement COLLECTION = elementUtils.getTypeElement("java.util.Collection");
TypeElement MAP = elementUtils.getTypeElement("java.util.Map");
TypeElement VOID = elementUtils.getTypeElement("java.lang.Void");
WildcardType WILDCARD_TYPE_NULL = typeUtils.getWildcardType(null, null);
Map<String,DeclaredType> cachedParentTypes = new HashMap<String, DeclaredType>();
...
public static boolean isA(TypeMirror type, TypeElement typeElement) {
// Have we used this type before?
DeclaredType parentType = cachedParentTypes.get(typeElement.getQualifiedName().toString());
if (parentType == null) {
// How many generic type parameters does this typeElement require?
int genericsCount = typeElement.getTypeParameters().size();
// Fill the right number of types with nulls
TypeMirror[] types = new TypeMirror[genericsCount];
for (int i = 0; i < genericsCount; i++) {
types[i] = WILDCARD_TYPE_NULL;
}
// Locate the correct DeclaredType to match with the type
parentType = typeUtils.getDeclaredType(typeElement, types);
// Remember this DeclaredType
cachedParentTypes.put(typeElement.getQualifiedName().toString(), parentType);
}
// Is the given type able to be assigned as the typeElement?
return typeUtils.isAssignable(type, parentType);
}
который я призываю как
if (isA(elType, VOID)) {
isVoid = true;
} else if (isA(elType, COLLECTION) || elType.getKind() == TypeKind.ARRAY) {
isCollectionOrArray = true;
} else if (isA(elType, MAP)){
isMap = true;
}