如何故意导致自定义的java编译器警告消息?

我正在做一个丑陋的临时黑客,以解决一个阻塞问题,而我们等待一个外部资源被修复。 除了用一个大的吓人的评论和一堆FIXMEs来标记,我很想让编译器提醒一个明显的警告信息,所以我们不要忘记把它写出来。 例如,像这样的东西:

[javac] com.foo.Hacky.java:192: warning: FIXME temporary hack to work around library bug, remove me when library is fixed! 

有没有一种方法,我可以导致一个故意的编译器警告与我select的消息? 如果做不到这一点,那么添加到代码中的最简单的东西就是抛出一个已经存在的警告,在违规行中可能会有一个string中的消息,这样就会在警告消息中打印出来?

编辑:过时的标签似乎没有为我做任何事情:

 /** * @deprecated "Temporary hack to work around remote server quirks" */ @Deprecated private void doSomeHackyStuff() { ... } 

eclipse中没有编译器或运行时错误,或者来自Sun的javac 1.6(从ant脚本运行),它肯定会执行这个函数。

我见过的一种技术是将它与unit testing结合起来(你unit testing吧?)。 基本上你创build了一个unit testing,一旦外部资源修复完成就失败了。 然后你评论一下这个unit testing,告诉别人如何解决这个问题。

关于这种方法的真正意义在于,解决黑客攻击的触发器是解决核心问题本身。

我认为一个自定义的注释,将由编译器处理,是解决scheme。 我经常编写自定义注释在运行时执行,但是我从来没有试过在编译时使用它们。 所以,我只能给你指点你可能需要的工具:

  • 编写一个自定义的注释types。 本页介绍如何编写注释。
  • 编写一个注释处理器,处理您的自定义注释以发出警告。 运行这种注释处理器的工具称为APT。 你可以在这个页面上find一个indroduction。 我认为你在APT API中需要的是AnnotationProcessorEnvironment,它会让你发出警告。
  • 从Java 6开始,APT被集成到了javac中。 也就是说,您可以在javac命令行中添加批注处理器。 javac手册的这一部分将告诉你如何调用你的自定义注释处理器。

我不知道这个解决scheme是否真的可行。 当我find一些时间的时候,我会尝试自己实现它。

编辑

我成功实施了我的解决scheme 作为奖励,我使用java的服务提供者工具来简化它的使用。 其实,我的解决scheme是一个包含2个类的jar:自定义注释和注释处理器。 要使用它,只需将此jar添加到项目的类path中,并注释任何你想要的! 这在我的IDE(NetBeans)中正常工作。

注释代码:

 package fr.barjak.hack; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE}) public @interface Hack { } 

处理器代码:

 package fr.barjak.hack_processor; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; @SupportedAnnotationTypes("fr.barjak.hack.Hack") public class Processor extends AbstractProcessor { private ProcessingEnvironment env; @Override public synchronized void init(ProcessingEnvironment pe) { this.env = pe; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { for (TypeElement te : annotations) { final Set< ? extends Element> elts = roundEnv.getElementsAnnotatedWith(te); for (Element elt : elts) { env.getMessager().printMessage(Kind.WARNING, String.format("%s : thou shalt not hack %s", roundEnv.getRootElements(), elt), elt); } } } return true; } } 

要将生成的jar作为服务提供者,在jar文件中添加META-INF/services/javax.annotation.processing.Processor文件。 该文件是一个acsii文件,必须包含以下文本:

 fr.barjak.hack_processor.Processor 

一个好的hack应该是另一个…我通常通过在hacky方法中引入一个未使用的variables来为描述的目的生成编译器警告,因此:

  / **
  * @deprecated“暂时破解远程服务器怪癖”
  * /
 @Deprecated
私人无效doSomeHackyStuff(){
     int FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed;
     ...
 }

这个未使用的variables会产生一个警告(取决于你的编译器),看起来像这样:

 警告:本地variablesFIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed从不读取。 

这个解决scheme不如自定义注释好,但它的优点是不需要预先准备(假设编译器已经configuration为对未使用的variables发出警告)。 我build议这种方法只适用于短命的黑客。 对于长期的黑客来说,我认为创build自定义注释的努力是合理的。

一些快速和不那么脏的方法,可能会使用@SuppressWarnings注释与故意错误的String参数:

 @SuppressWarnings("FIXME: this is a hack and should be fixed.") 

这会产生一个警告,因为它不被编译器识别为一个特定的警告来压制:

不受支持@SuppressWarnings(“FIXME:这是一个黑客,应该修复”)

如何将方法或类标记为@Deprecated? 文档在这里 。 请注意,同时存在@Deprecated和@deprecated – 大写D是注释,小写d是javadoc版本。 javadoc版本允许你指定一个任意的string来解释发生了什么。 但编译器在看到它时不需要发出警告(虽然很多)。 注释应该总是引起警告,尽pipe我不认为你可以添加一个解释。

这里的更新是我刚刚testing的代码:Sample.java包含:

 public class Sample { @Deprecated public static void foo() { System.out.println("I am a hack"); } } 

SampleCaller.java包含:

 public class SampleCaller{ public static void main(String [] args) { Sample.foo(); } } 

当我运行“javac Sample.java SampleCaller.java”我得到以下输出:

 Note: SampleCaller.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. 

我正在使用Sun的javac 1.6。 如果你想要一个诚实善良的警告,而不是一个便条,使用-Xlint选项。 也许这将正确渗透通过ant。

我写了一个库,这样做注释: 轻量级Javac @Warning注解

用法很简单:

 // some code... @Warning("This method should be refactored") public void someCodeWhichYouNeedAtTheMomentButYouWantToRefactorItLater() { // bad stuff going on here... } 

编译器会在你的文本中发出警告信息

我们可以用注释来做到这一点!

要引发错误,请使用Messager发送带有Diagnostic.Kind.ERROR的消息。 简短的例子:

 processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, "Something happened!", element); 

这是我写的一个相当简单的注释来testing。

这个@Marker注释表明目标是一个标记接口:

 package marker; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Marker { } 

如果不是,则注释处理器会导致错误:

 package marker; import javax.annotation.processing.*; import javax.lang.model.*; import javax.lang.model.element.*; import javax.lang.model.type.*; import javax.lang.model.util.*; import javax.tools.Diagnostic; import java.util.Set; @SupportedAnnotationTypes("marker.Marker") @SupportedSourceVersion(SourceVersion.RELEASE_6) public final class MarkerProcessor extends AbstractProcessor { private void causeError(String message, Element e) { processingEnv.getMessager() .printMessage(Diagnostic.Kind.ERROR, message, e); } private void causeError( Element subtype, Element supertype, Element method) { String message; if (subtype == supertype) { message = String.format( "@Marker target %s declares a method %s", subtype, method); } else { message = String.format( "@Marker target %s has a superinterface " + "%s which declares a method %s", subtype, supertype, method); } causeError(message, subtype); } @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Elements elementUtils = processingEnv.getElementUtils(); boolean processMarker = annotations.contains( elementUtils.getTypeElement(Marker.class.getName())); if (!processMarker) return false; for (Element e : roundEnv.getElementsAnnotatedWith(Marker.class)) { ElementKind kind = e.getKind(); if (kind != ElementKind.INTERFACE) { causeError(String.format( "target of @Marker %s is not an interface", e), e); continue; } if (kind == ElementKind.ANNOTATION_TYPE) { causeError(String.format( "target of @Marker %s is an annotation", e), e); continue; } ensureNoMethodsDeclared(e, e); } return true; } private void ensureNoMethodsDeclared( Element subtype, Element supertype) { TypeElement type = (TypeElement) supertype; for (Element member : type.getEnclosedElements()) { if (member.getKind() != ElementKind.METHOD) continue; if (member.getModifiers().contains(Modifier.STATIC)) continue; causeError(subtype, supertype, member); } Types typeUtils = processingEnv.getTypeUtils(); for (TypeMirror face : type.getInterfaces()) { ensureNoMethodsDeclared(subtype, typeUtils.asElement(face)); } } } 

例如,这些是@Marker正确用法:

  •  @Marker interface Example {} 
  •  @Marker interface Example extends Serializable {} 

@Marker这些用法会导致编译器错误:

  •  @Marker class Example {} 
  •  @Marker interface Example { void method(); } 

    标记错误

这里有一篇博客文章,我发现这个主题很有帮助:

  • 使用Java语言中的Annotation Processors进行代码生成

这里给出了一个关于注释的教程,最后给出了一个定义你自己的注释的例子。 不幸的是,教程的快速浏览说,那些只在javadoc中可用…

编译器使用的注释有三种注释types由语言规范本身预定义:@Deprecated,@Override和@SuppressWarnings。

所以看起来,你所能做的只是抛出一个@Deprecated标签,编译器会打印出来,或者在javadoc中join一个自定义的标签来说明这个黑客行为。

你应该使用一个工具来编译,就像ant ou maven一样。 有了它,你应该在编译时定义一些任务,例如可以产生一些关于你的FIXME标签的日志(比如消息或者警告)。

如果你想要一些错误,那也是可能的。 像在代码中留下一些TODO时停止编译(为什么不呢?)

为了得到任何警告,我发现未使用的variables和自定义的@SuppressWarnings不适用于我,但是不必要的转换就是:

 public class Example { public void warn() { String fixmePlease = (String)"Hello"; } } 

现在当我编译时:

 $ javac -Xlint:all Example.java ExampleTest.java:12: warning: [cast] redundant cast to String String s = (String) "Hello!"; ^ 1 warning 

如果你使用的是IntelliJ。 你可以去:Preferences> Editor> TODO并添加“\ bhack.b *”或其他任何模式。

如果您随后发表评论,例如// HACK: temporary fix to work around server issues

然后在TODO工具窗口中,它会很好地显示,以及所有其他定义的模式,同时进行编辑。