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

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

问题描述

如果我有一个 javax.lang.model.element.VariableElement 表示一个 List< SomeObject> 类型的变量和一个 javax.lang.model.element.ExecutableElement 表示一种方法,该方法的一个参数类型为 List< ;?扩展SomeObject> ,如何验证我的变量类型可以传递给我的方法类型?

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.

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

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
  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 框架中进行编程的,因此我假设您了解实用程序类

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

上面的代码中的 c1 c2 指的是最特定的类类型,这些类将在运行时包含您的变量和方法.例如,您可能具有以下类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"));

变量和方法参数的类型将取值为 BigInteger Byte ,就好像没有通用的参数开头!

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< ;?类型吗?扩展Date> 和类似的方法:

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;

无论如何,如何检索TypeMirror, 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天全站免登陆