2012-10-05 13 views
11

Al escribir un procesador de anotaciones usando la API de Java 6, me encontré con la necesidad de manejar todos los mapas de una manera particular, pero claramente estoy entendiendo mal lo que la API pretende hacer o cómo invocar eso. Aquí está el código que me está haciendo infeliz:Tipos isAssignable and isSubtype malentendido

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)); 
} 

Teniendo en cuenta que, aquí está la salida de la misma:

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 

Esto tiene mucho sentido para mí, excepto para el primer bloque en el que cabría esperar de una El elemento HashMap se puede asignar a Map, y espero que HashMap sea un subtipo de Map.

¿Qué me falta aquí?

Respuesta

9

Sospecho que es debido a las variables de tipo. HashMap<String, String> se puede asignar al Map<String, String>, pero sin la creación de instancias concretas de las variables de tipo no se puede asegurar que un HashMap<A,B> arbitrario se pueda asignar al Map<X,Y>.

Si usted instancia las variables con comodines entonces usted debe conseguir el resultado que espera

DeclaredType wildcardMap = typeUtils.getDeclaredType(
    elementUtils.getTypeElement("java.util.Map"), 
    typeUtils.getWildcardType(null, null), 
    typeUtils.getWildcardType(null, null)); 

Esto le dará el espejo tipo para Map<?,?>, que todas las instancias HashMap son asignables a.

+0

Ya sabes, pensé que, y pensé ya que las cartas genéricas apareadas que los tipos podrían coincidir, pero supongo que no. ¡Gracias! – Patrick

+1

Este enfoque completo y la implementación de @Patrick son demasiado complejos. Simplemente use 'types.isAssignable (type, types.erasure (baseGenerifiedType))' (por ejemplo, compruebe si el tipo es asignable a raw 'HashMap' en lugar de' HashMap ') – user1643723

2

actualización (marzo de 2016): Basado en un comentario por @ user1643723, parece que hay una función types.erasure(TypeMirror) biblioteca que no estaba al tanto de en 2012.


Basado en Ian's Answer, me Ahora estoy usando el siguiente método para unir los tipos base como describí para Map en la pregunta.

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); 
} 

el que invoco como

if (isA(elType, VOID)) { 
    isVoid = true; 
} else if (isA(elType, COLLECTION) || elType.getKind() == TypeKind.ARRAY) { 
    isCollectionOrArray = true; 
} else if (isA(elType, MAP)){ 
    isMap = true; 
}