34

StringJava では不変です。次のスニペットは、大まかに言えば「間違っています」。

String s = "hello world!";

s.toUpperCase(); // "wrong"!!

System.out.println(s); // still "hello world!"!!!

これは「間違っている」にもかかわらず、コードはコンパイルされて実行されます。おそらく多くの初心者は、間違いが何であるかを説明するか、ドキュメントを参照して自分で調べる必要があるため、混乱します。

ドキュメントを読むことは API を理解する上で不可欠ですが、コンパイル時のチェックを追加することでこれを補うことができるかどうか疑問に思っています。特に、Java の注釈フレームワークを使用して、特定のメソッドによって返される値が無視されないようにすることができるのではないかと考えています。API 設計者/ライブラリ作成者は、メソッドでこの注釈を使用して、どの戻り値を無視してはならないかを文書化します。

API がこの注釈 (またはおそらく別のメカニズム) で補足されると、ユーザーが上記のようなコードを記述しても、コンパイルされません (または厳しい警告が表示されます)。

それで、これを行うことができますか?


付録: 動機

一般的なケースでは、Javaがメソッドの戻り値を無視できるようにする必要があることは明らかです。List.add( always true )、 (previous value)などのメソッドの戻り値はSystem.setProperty、ほとんどの場合、おそらく安全に無視できます。

ただし、戻り値を無視してはならないメソッドも多数あります。そうすることは、ほとんどの場合、プログラマーのエラーであるか、API の適切な使用法ではありません。これらには次のようなものが含まれます。

  • 呼び出されたインスタンスを変更するのではなく、操作の結果を返す不変型 ( StringBigIntegerなど) のメソッド。
  • 戻り値が動作の重要な部分であり、無視されるべきではないメソッドですが、とにかく無視する人もいます (たとえばInputStream.read(byte[])、読み取られたバイト数を返します。これは、配列の全長であると想定されるべきではありません)。

現在、これらの戻り値を無視するコードを記述して、警告なしでコンパイルおよび実行することができます。静的分析チェッカー / バグ ファインダー / スタイル エンフォーサー / などは、ほぼ確実にこれらをコードの匂いの可能性としてフラグを立てることができますが、これが API 自体によって、おそらく注釈を通じて強制される場合は、適切/理想的であるように思われます。

クラスが常に「適切に」使用されることを保証することはほとんど不可能ですが、クライアントを適切に使用するように導くためにできることはあります (効果的な Java 2nd Edition、項目 58: 回復可能な条件にはチェック済みの例外を使用し、プログラミング エラーの実行時例外項目 62: 各メソッドによってスローされたすべての例外を文書化します)。クライアントが特定のメソッドの戻り値を無視しないように強制する注釈を持ち、コンパイル時にエラーまたは警告の形でコンパイラによって強制されるようにすることは、この考えに沿っているようです。


付録 2: スニペット

以下は、私が達成したいことを簡潔に説明する予備的な試みです。

@interface Undiscardable { }
//attachable to methods to indicate that its
//return value must not be discarded

public class UndiscardableTest {
     public static @Undiscardable int f() {
             return 42;
     }

     public static void main(String[] args) {
             f(); // what do I have to do so this generates
                  // compilation warning/error?

             System.out.println(f()); // this one would be fine!
     }
}

上記のコードは正常にコンパイルおよび実行されます ( ideone.com で見られるように)。そうならないようにするにはどうすればいいですか?必要なセマンティクスをどのように割り当てることができ@Undiscardableますか?

4

7 に答える 7

12

jsr305 をチェックアウトすることもできます。@CheckReturnValueアノテーションを定義します。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.annotation.meta.When;

@Documented
@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE,
        ElementType.PACKAGE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckReturnValue {
    When when() default When.ALWAYS;
}

これは findbugs と互換性があり、誰かが戻り値の処理を忘れた場合に警告を生成します。

Guavas Splitter はそれを使用します: http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Splitter.java

私は、静的コード分析を導くことができるアノテーションが大好きだと言わざるを得ません。

于 2012-03-08T19:59:21.177 に答える
8

特にポータブルな方法で実現可能かどうかはわかりませんが、Adrian Kuhnの Java ( GitHub コード)の Roman Numerals を見てください。彼は注釈処理Sun のプライベート API を使用して、ソース コードにアクセスして置換を行うことにより、 Java にローマ数字リテラルを追加しました。 javac

おそらく、同様のアプローチを次のように使用できます。

  • ソースコードで注釈付きメソッドの呼び出しを見つける
  • 結果が割り当てられているかどうかを確認します(簡単なIMOではありません)
  • そうでない場合は、コンパイラの警告を生成します

また、Adrian の投稿にある次のリソースもお見逃しなく。

あなたも好きかも

参照

関連する質問

于 2010-09-01T02:39:07.933 に答える
2

@Deprecated要するに、結果を代入せずにメソッドが呼び出されたときに、コンパイラ/IDE が警告/エラーを発生させるのに役立つようなアノテーションが必要ですか? これは、Java ソース コードとコンパイラを変更しないと実現できません。特定のメソッドに注釈を付ける必要があり、コンパイラはそれらを認識している必要があります。ソースやコンパイラを変更せずに、ケースを認識し、それに応じてエラー/警告を生成する種類の IDE プラグイン/設定を作成できます。


更新: 呼び出されたメソッドとそれに応じてエラーをチェックするフレームワーク/プラグインを書くことができます。実行時にのみアノテーションを利用できるようにしたいと考えています。これを行うには、 を使用して注釈に注釈を付けます。次に、 を使用して、この注釈が使用可能かどうかを判断できます。そのようなフレームワークがこの仕事をどのように行うことができるかのキックオフの例を次に示します。@Retention (RetentionPolicy.RUNTIME)Method#getAnnotation()

package com.example;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class Test {

    public static void main(String[] args) throws Exception {
        if (Test.class.getMethod("f", new Class[0]).getAnnotation(Undiscardable.class) != null) {
            System.err.println("You should not discard the return value of f()!");
        } else {
            f();
        }

        System.out.println(f());
    }

    public static @Undiscardable int f() {
        return 42;
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Undiscardable {}

それでも、代わりにコンパイラーに仕事をさせるには、もう少し作業を行う必要があります。

于 2010-09-01T00:28:38.997 に答える
1

注釈を定義する必要はありません。メソッドが呼び出されたときのルールを定義できます。

  1. メソッドの戻り値の型が void です。
  2. メソッドの結果は、別のメソッド呼び出しの引数として使用されます。また
  3. メソッドの結果は変数に割り当てられます。

このルールを適用する Processor を実装するか、このルールを適用する Checkstyle を実装できます。

于 2010-09-01T01:02:14.610 に答える
0

問題があります。問題は、人々がメソッドの戻り値を誤って使用するのを忘れる可能性があることです。注釈を使用することで、ライブラリの作成者に、特定のメソッドの結果を破棄しないように呼び出し元に思い出させる責任があることを伝えることになります。

良いアイデアのように思えますが、そうではないと思います。ユーザーの不適切な実践についての通知でコードを乱雑にしたいですか? Lint、Sonar、JavaDocs など、コードを調べて何か間違ったこと (または望ましくないこと) を行っていることを知らせる製品はたくさんあります。

ライブラリの作成者が言ったことに同意しない場合は、@SuppressWarnings("return-discarded") を使用する必要がありますか?

これは学習支援として役立つかもしれませんが、私のポイントは、初心者プログラマーを支援することよりも、関心の分離に関係していることです。クラスのコード (および注釈) は、クラスの機能に関連している必要があり、そのメソッドをいつどのように使用するかのポリシーを設定する必要はありません。

于 2016-09-26T13:24:38.823 に答える