88

外部リソースが修正されるのを待つ間、ブロッキングの問題を回避するために、醜い一時的なハックをコミットしようとしています。大きな恐ろしいコメントとたくさんのFIXMEでマークすることは別として、私はコンパイラーにリマインダーとして明白な警告メッセージをスローさせたいので、これを取り除くことを忘れないでください。たとえば、次のようになります。

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

選択したメッセージで意図的なコンパイラ警告を発生させる方法はありますか?それができない場合、既存の警告をスローするためにコードに追加する最も簡単なことは何ですか?おそらく、問題のある行に文字列内のメッセージがあり、警告メッセージに出力されますか?

編集:非推奨のタグは私のために何もしていないようです:

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

Eclipseまたはsunjavac1.6(antスクリプトから実行)でコンパイラーまたはランタイム・エラーは発生せず、関数を確実に実行しています。

4

11 に答える 11

89

コンパイラによって処理されるカスタムアノテーションが解決策だと思います。実行時に何かを行うためにカスタムアノテーションを頻繁に作成しますが、コンパイル時にそれらを使用しようとしたことはありません。だから、私はあなたが必要とするかもしれないツールへのポインタをあなたに与えることができるだけです:

  • カスタム注釈タイプを記述します。このページでは、注釈の書き方について説明します。
  • カスタム注釈を処理して警告を発する注釈プロセッサを作成します。このような注釈プロセッサを実行するツールは、APTと呼ばれます。このページで紹介を見つけることができます。APT APIに必要なのは、警告を発することができるAnnotationProcessorEnvironmentだと思います。
  • Java 6から、APTはjavacに統合されています。つまり、javacコマンドラインで注釈プロセッサを追加できます。javacマニュアルのこのセクションでは、カスタムアノテーションプロセッサを呼び出す方法について説明します。

この解決策が本当に実行可能かどうかはわかりません。時間があれば自分で実装してみます。

編集

ソリューションの実装に成功しました。そしてボーナスとして、私はJavaのサービスプロバイダー機能を使用してその使用を簡素化しました。実際、私のソリューションは、カスタムアノテーションとアノテーションプロセッサの2つのクラスを含むjarです。これを使用するには、プロジェクトのクラスパスにこのjarを追加し、必要なものに注釈を付けます。これは私の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
于 2009-11-18T09:48:38.060 に答える
42

私が見たテクニックの 1 つは、これを単体テストに結び付けることです (単体テストを行いますよ?)。基本的に、外部リソースの修正が完了すると失敗する単体テストを作成します。次に、その単体テストにコメントして、問題が解決したら危険なハックを元に戻す方法を他の人に伝えます。

このアプローチの優れている点は、ハッキングを元に戻すトリガーが、コアの問題自体の修正であるということです。

于 2009-11-18T02:44:17.213 に答える
15

いくつかの迅速でそれほど汚れていないアプローチは、意図的に間違った引数@SuppressWarningsで注釈を使用することです。String

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

これは、コンパイラによって抑制すべき特定の警告として認識されないため、警告が生成されます。

サポートされていない @SuppressWarnings("FIXME: これはハックであり、修正する必要があります。")

于 2015-02-20T21:16:34.743 に答える
12

良いハックには別のハックが必要です...私は通常、ハック メソッドに未使用の変数を導入することで、説明した目的でコンパイラの警告を生成します。

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

この未使用の変数は、(コンパイラによっては) 次のような警告を生成します。

警告: ローカル変数 FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed は読み取られません。

このソリューションは、カスタム アノテーションほど優れたものではありませんが、事前の準備が不要であるという利点があります (未使用の変数に対して警告を発行するようにコンパイラが既に構成されていると仮定します)。このアプローチは、短期間のハッキングにのみ適していることをお勧めします。長期にわたるハッキングの場合、カスタム アノテーションを作成する努力は正当化されると私は主張します。

于 2011-07-21T17:43:33.990 に答える
5

メソッドまたはクラスを@Deprecatedとしてマークするのはどうですか?ここのドキュメント。@Deprecatedと@deprecatedの両方があることに注意してください。大文字のDバージョンは注釈であり、小文字のdはjavadocバージョンです。javadocバージョンでは、何が起こっているかを説明する任意の文字列を指定できます。しかし、コンパイラーはそれを見たときに警告を出す必要はありません(多くはそうしますが)。注釈は常に警告を表示するはずですが、説明を追加することはできないと思います。

ここでの更新は、私がテストしたばかりのコードです。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();
     }
}

「javacSample.javaSampleCaller.java」を実行すると、次の出力が得られます。

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

私はsunのjavac1.6を使用しています。単なるメモではなく、正直な善意の警告が必要な場合は、-Xlintオプションを使用してください。たぶんそれはAntを適切に浸透するでしょう。

于 2009-11-17T23:30:23.340 に答える
4

アノテーションでこれを行うことができます!

エラーを発生させるには、 を使用Messagerしてメッセージを送信しますDiagnostic.Kind.ERROR。短い例:

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

これをテストするためだけに書いたかなり単純な注釈を次に示します。

この@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();
    }
    

    マーカーエラー

これは、このテーマを始めるのに非常に役立つブログ投稿です。


小さな注意: 以下のコメンターが指摘しているのは、MarkerProcessor参照Marker.classのため、コンパイル時の依存関係があるということです。上記の例は、両方のクラスが同じ JAR ファイル (たとえばmarker.jar) に入るという前提で書きましたが、常に可能であるとは限りません。

たとえば、次のクラスを持つアプリケーション JAR があるとします。

com.acme.app.Main
com.acme.app.@Ann
com.acme.app.AnnotatedTypeA (uses @Ann)
com.acme.app.AnnotatedTypeB (uses @Ann)

次に、プロセッサ@Annは別の JAR に存在し、アプリケーション JAR のコンパイル中に使用されます。

com.acme.proc.AnnProcessor (processes @Ann)

その場合、循環 JAR 依存関係が作成されるためAnnProcessor、 のタイプを直接参照することはできません。名前または/で@Annのみ参照できます。@AnnStringTypeElementTypeMirror

于 2015-04-26T18:12:16.770 に答える
2

ここでは注釈に関するチュートリアルを示し、下部に独自の注釈を定義する例を示します。残念ながら、チュートリアルをざっと見てみると、それらは javadoc でのみ利用可能であることがわかりました...

コンパイラで使用される注釈 @Deprecated、@Override、および @SuppressWarnings の 3 つの注釈タイプが言語仕様自体によって事前定義されています。

したがって、実際にできることは、コンパイラが出力する @Deprecated タグを挿入するか、ハックについて説明するカスタム タグを Javadoc に挿入することだけのようです。

于 2009-11-17T23:57:08.640 に答える
0

ant ou maven などのツールを使用してコンパイルする必要があります。これを使用して、たとえば、FIXME タグに関するログ (メッセージや警告など) を生成できるいくつかのタスクをコンパイル時に定義する必要があります。

また、エラーが必要な場合は、それも可能です。コードに TODO を残したときにコンパイルを停止するのと同じように (なぜですか?)

于 2009-11-17T23:46:50.283 に答える
0

警告を表示するには、未使用の変数とカスタム @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
于 2016-12-23T14:45:59.967 に答える