如何验证 java VariableElement 可以传递给 ExecutableElement? [英] How to verify a java VariableElement can be passed to an ExecutableElement?

查看:17
本文介绍了如何验证 java VariableElement 可以传递给 ExecutableElement?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有一个 javax.lang.model.element.VariableElement 表示 List 类型的变量和一个 javax.lang.model.element.ExecutableElement 表示具有一个 List,如何验证我的变量类型可以传递到我的方法类型中?

If I have a javax.lang.model.element.VariableElement that represents a variable of type List<SomeObject>, and an javax.lang.model.element.ExecutableElement that represents a method with one argument of type List<? extends SomeObject>, how can I verify that my variable type can be passed into my method type?

我开始尝试编写一个 javax.lang.model.type.TypeVisitor 来尝试比较忽略通配符类型的类型,但我不确定是否有更简单的方法.

I started to try and write a javax.lang.model.type.TypeVisitor to try and compare types ignoring wildcard types, but I'm not sure if there's an easier way.

推荐答案

显而易见,但有点无用的解决方案:让方法调用生成(我假设你正在做),等到最后一轮编译并检查是否通过 RoundEnvironment#errorRaised() 的返回值发生错误.之后您可能无法生成任何来源.

Obvious, but kinda useless solution: let the method call be generated (which I am assuming you to be doing), wait until last compilation round and check if an error occurred via return value of RoundEnvironment#errorRaised(). You may not be able to generate any sources afterwards.

如果您真的想为呃……科学进行验证,请参见下文.

If you really want to perform that verification for ugh… science, see below.

您应该已经了解,技术上不可能将一个元素分配给另一个元素.你必须从元素到类型(例如 TypeMirrors).这样做时要非常小心!

As you should already understand, it is technically not possible to assign an element to to another element. You have to go from elements to types (e.g. TypeMirrors). Be very careful while doing that!

  1. 在继续之前始终检查源代码是否有错误.ErrorType 很疯狂:编译器会让它参与奇怪的赋值,并且通常将其视为某种神奇的任何事情都可以"通配符(不要与实际的 WildcardType 混淆).我个人使用的是稍微修改过的 Google Auto SuperficialValidaton 对此,您也应该如此,除非其他人已经为您验证了所有元素.
  2. 永远不要假设一个元素具有某种特定类型的类型(例如 DeclaredType).如果这样做,您的代码将很难重构并适应新的语言功能.
  3. 使用TypeMiror#getKind 来检查特定的类型种类并在有意义时转换为 TypeMirror 的特定子类型.访问者范式可以简化前向兼容性,但是当您需要特定类型类型(如 DeclaredType)来继续时,不应该使用它.
  1. Always check source code for errors before proceeding. ErrorType is crazy: the compiler will let it to participate in strange assignments and generally treat it as some magical "anything will do" wildcard (not to confuse with actual WildcardType). I am personally using slightly modified version of Google Auto SuperficialValidaton for this, and so should you, unless somebody else have already validated all elements for you.
  2. Never assume, that an element have type of some specific kind (e.g. DeclaredType). Your code will be very hard to refactor and adapt to new language features if you do.
  3. Do use TypeMiror#getKind to check for specific type kinds and cast to specific subtypes of TypeMirror when that makes sense. Visitor paradigm is there to make forward compatibility easy, but you shouldn't use it when you need a specific type kind like DeclaredType to proceed.

由于您在 java.lang.model 框架内编程,我假设您了解实用程序类 类型Elements 以及如何获取它们的实例.让我们称这些实例为 typeselements.

Since you are programming within java.lang.model framework, I will assume, that you know about utility classes Types and Elements and how to get instances of them. Let's call those instances types and elements.

您还没有发布具体的代码,也没有说明您的方法和变量来自哪里以及它们是什么样的,所以这里是可以想象到的最通用的方法:

You haven't posted specific code and haven't told anything about where do your method and variable come from and what are they like, so here is the most generic approach imaginable:

VariableElement variable = ...
ExecutableElement method = ...

TypeMirror variableType = types.asMemberOf(c1, variable);
ExecutableType methodType = (ExecutableType) types.asMemberOf(c2, method);

上面代码中的 c1c2 指的是最具体的类,它们将在运行时包含您的变量和方法.例如,您可能有这样的类 c1 和 c2:

The c1 and c2 in the code above refer to the most specific types of classes, that will contain your variable and method at runtime. For example you may have classes c1 and c2 like these:

class c1Base<X extends Number> {
  protected X variable;
}

interface c2Base<Y extends Number> {
  void method(Y parameter);
}

class c1 extends c1Base<BigInteger> {}

abstract class c2 implements c2Base<Byte> {}

你得到 c1 和 c2 的类型:

You get types of c1 and c2:

// If you happen to know, that c1 and c2 have their own type arguments,
// and which type arguments may be used at run time, specify those here

DeclaredType c1 = types.getDeclaredType(elements.getTypeElement("co.exampl.c1"));

DeclaredType c2 = types.getDeclaredType(elements.getTypeElement("co.exampl.c2"));

变量和方法参数的类型将评估为 BigIntegerByte,就好像没有通用参数开始一样!

And types of variable and method parameter will evaluate to BigInteger and Byte, as if there were no generic parameters to begin with!

当然,您也可能碰巧遇到这样一种情况,当提炼掉类型参数没有意义时:您通过调用 getEnclosedElements 收到了方法和变量,但不知道容器类型是什么可以在运行时使用.在这种情况下,只需使用 asType:

Naturally, you may also happen to be in a situation, when refining away type parameters does not make sense: you have received both method and variable by calling getEnclosedElements and have no clue, what container type can possibly be used at runtime. In that case just use asType:

TypeMirror variableType = variable.asType();
ExecutableType methodType = (ExecutableType) method.asType();

检查变量的类型是否可以赋值给参数的类型

Javac 是否允许将 X 类型的变量传递给参数类型为 Y 的方法基于两个因素:

Check if type of variable can be assigned to type of parameter

Whether Javac allows variable of type X to be passed to method with parameter type Y is based on two factors:

  1. 在一般情况下,X 可以分配给 Y 吗?
  2. 大量的类型推断魔法.

如果可以在不涉及额外因素的情况下将 X 分配给 Y,则方法调用肯定也会成功:

If X can be assigned to Y without extra factors involved, the method call is surely going to succeed too:

TypeMirror methodParameterType = methodType.getParameterTypes().get(0);
if (types.isAssignable(variableType, methodParameterType)) {
  // return early, the call will be allowed
}

但如果不是,但由于类型推断,调用可能会成功怎么办?不幸的是,从 Java 8 开始,似乎没有用于访问编译器类型推断的公共 API,也没有 java.lang.model 类在幕后使用它.你可以有一个类型 List 和这样的方法:

But what if it isn't, but the call may succeed anyway because of type inference? Unfortunately, as of Java 8 there seems to be no public API for accessing compiler's type inference and no java.lang.model classes use it under the hood. You can have a type List<? extends Date> and a method like this:

<X extends Serializable> void foobar(List<X> parameter) throws RemoteException;

无论你如何检索 TypeMirrors,isAssignable 都不会返回 true,尽管可以使用这种类型的参数调用该方法.

and no matter, how you go about retrieving TypeMirrors, isAssignable won't return true, despite the fact, that the method can be called with parameter of such type.

Java 规范的部分,关于类型推断,它是巨大的,并且使用了许多工具,通过公共 API(计算最小上限等)注释处理器不可用.它也有在主要语言修订版中发生很大变化的趋势.自己实现相应的逻辑是非常不明智的.

The part of Java specification, concerning type inference, is huge and uses many facilities, not available to annotation processors via public API (computing least upper bound etc.) It also has tendency to change a lot in major language revisions. Implementing the corresponding logic yourself would be highly unwise.

如果我是你,我会放弃尝试提前进行最终验证,而是让 Javac 自己做出决定.如果用户在某处提供了不正确的类型,只需生成方法调用并让它失败.您可以通过检查变量类型和方法参数类型是否都是 DECLARED 并且都没有任何类型参数来改进您的早期预测,但如果不是这种情况,仍然允许生成调用:

If I were you, I would give up on trying to perform definitive verification in advance and instead let Javac itself make a decision. Just generate the method call and let it fail if user has supplied incorrect types somewhere. You can improve your early prediction by checking if variable type and method parameter type are both DECLARED and neither has any type arguments, but still allow the call to be generated if that is not the case:

if (methodParameterType.getKind() == TypeKind.DECLARED
  && variableType.getKind() == TypeKind.DECLARED
  && types.isSameType(methodParameterType, types.erasure(methodParameterType))
  && types.isSameType(variableType, types.erasure(variableType))) {
  // type inference is extremely unlikely to interfere
  return types.isAssignable(variableType, methodParameterType);
}

// allowed the call ourselves, if compiler forbids it, it will emit
// a compilation error with descriptive message, so user should probably
// be able to figure things out

这篇关于如何验证 java VariableElement 可以传递给 ExecutableElement?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆