I have the name of a variable/identifier, say, x
, and the JCCompilationUnit
and Scope
. Is there a way to find the type of x
?
public Symbol getSymbol(CompilationUnitTree cut, JCStatement stmt, List<JCExpression> typeParams, Name varName, List<JCExpression> args) {
java.util.List<Type> typeSyms = getArgTypes(typeParams, cut, stmt);
java.util.List<Type> argsSyms = getArgTypes(args, cut, stmt);
final Scope scope = getScope(cut, stmt);
Symbol t = contains(scope, typeSyms, varName, argsSyms); //first lookup scope for all public identifiers
TypeElement cl = scope.getEnclosingClass();
while (t == null && cl != null) { //lookup hierarchy for inacessible identifiers too
t = contains(elementUtils.getAllMembers(cl), typeSyms, varName, argsSyms);
final TypeMirror superclass = cl.getSuperclass();
if (superclass != null) {
cl = (TypeElement) ((Type) superclass).asElement();
}
}
return t;
}
public Symbol getSymbol(Name varName, Symbol accessor, CompilationUnitTree cut, JCStatement stmt) {
if (varName.contentEquals("class")) {
Symbol javaLangClassSym = getSymbol(cut, stmt, null, elementUtils.getName("java.lang.Class"), null);
JCIdent id = tm.Ident(javaLangClassSym);
JCExpression mName = tm.Select(id, elementUtils.getName("forName"));
JCLiteral idLiteral = tm.Literal(accessor.toString());
JCMethodInvocation mi = tm.Apply(List.<JCExpression>nil(), mName, List.<JCExpression>of(idLiteral));
Symbol s = getSymbol(mi, cut, stmt);
return s;
}
accessor = getTypeSymbol(accessor);
java.util.List<Symbol> enclosedElements = getEnclosedElements(accessor, cut, stmt);
Symbol s = contains(enclosedElements, null, varName, null);
return s;
}
I ended up writing a full class with resolving methods. Inheriting it, or passing in the TreeMaker (and other parameters) it can be made static. Should someone find it worthwile, a patch is welcome.
The Scope
object has a method getLocalElements()
, which can be iterated over. Each element can then be asked by its name, and when this is the right one (and it is a variable, too), you can get its type.
This is the concept, untested:
private final static Set<ElementKind> variableKinds =
Collections.unmodifiableSet(EnumSet.of(ElementKind.FIELD, ElementKind.ENUM_CONSTANT,
ElementKind.PARAMETER, ElementKind.LOCAL_VARIABLE));
public Type getTypeOfVariable(Scope scope, String varName)
{
for(Element e : scope.getLocalElements()) {
if(variableKinds.contains(e.getKind()) && e.getName().equals(varName)) {
return e.getType();
}
}
throw new NoSuchElementException("No variable " + varName + " in " + scope);
}
Edit: Yeah, really untested (there is no getType()
method).
So, how to get from a Element
(or VariableElement) from its type?
The Trees
class has some utility methods allowing the retrieval of a Tree
or TreePath
from the Element
, and thus we can get a VariableTree
(since this came from a variable declaration). The VariableTree
now has a getType()
method, which returns a Tree
- but in fact this is one of PrimitiveTypeTree
, ParametrizedTypeTree
, ArrayTypeTree
and IdentifierTree
(for simple reference types as well as type variables). So, if you only want to print the type, this may be enough. Otherwise, again with the Trees
class we now can get a TypeMirror for this type.
(I did something similar once when I tried to write a new Doclet which would also output the formatted source code. Terrible flipping between all those APIs.)
来源:https://stackoverflow.com/questions/4881389/how-do-i-find-the-type-declaration-of-an-identifier-using-the-java-tree-compiler