21

Eclipseでいくつかの新しい警告設定を行いました。これらの新しい設定で、私は奇妙な警告に直面しています。読んだ後、私はそれが何であるかを知るようになりましたが、それを削除する方法を見つけることができませんでした。

これがサンプルコードに関する私の問題です

public class Test {
    private String testString;

    public void performAction() {

        new Thread( new Runnable() {
            @Override
            public void run() {
                testString = "initialize"; // **
            }
        });
    }
}

* *の行は、Eclipseで警告を表示します

Read access to enclosing field Test.testString is emulated by a synthetic accessor method. 
Increasing its visibility will improve your performance.

問題は、のアクセス修飾子を変更したくないということですtestString。また、そのためのゲッターを作成したくありません。

どのような変更を行う必要がありますか?


More descriptive example 

public class Synthetic
{
    private JButton testButton;

    public Synthetic()
    {
        testButton = new JButton("Run");
        testButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae)
                {
                    /* Sample code */
                    if( testButton.getText().equals("Pause") ) {
                        resetButton(); // **    
                    } else if( testButton.getText().equals("Run") ) {
                        testButton.setText("Pause"); // **  
                    }

                }
            }
        );
    }

    public void reset() {
        //Some operations
        resetButton();
    }

    private void resetButton() {
        testButton.setText("Run");
    }
}

の行**は私に同じ警告を与えます。

4

5 に答える 5

46

「合成」法とは何ですか?

Methodクラス(およびその親)から始めて、Member合成メンバーが「コンパイラーによって導入された」こと、およびJLS§13.1が詳細を教えてくれることを学びます。それは注意します:

Javaコンパイラによって発行されたコンストラクトは、ソースコードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合は、合成としてマークする必要があります。

このセクションではバイナリ互換性について説明しているため、JVMも参照する価値があり、JVM§4.7.8はもう少しコンテキストを追加します。

ソースコードに表示されないクラスメンバーは、Synthetic属性を使用してマークする必要があります。そうでない場合は、ACC_SYNTHETICフラグを設定する必要があります。この要件の唯一の例外は、実装アーティファクトとは見なされないコンパイラ生成メソッドです。

このSynthetic属性は、ネストされたクラスとインターフェースをサポートするためにJDK1.1で導入されました。

言い換えると、「合成」メソッドは、JVM自体がサポートしていない言語機能をサポートするためにJavaコンパイラーが導入する実装アーティファクトです。

どうしたの?

あなたはそのようなケースに遭遇しています。private匿名の内部クラスからクラスのフィールドにアクセスしようとしています。Java言語はこれを許可しますが、JVMはそれをサポートしないため、Javaコンパイラは、privateフィールドを内部クラスに公開する合成メソッドを生成します。コンパイラは他のクラスがこのメソッドを呼び出すことを許可しないため、これは安全ですが、2つの(小さな)問題が発生します。

  1. 追加のメソッドが宣言されています。これはほとんどのユースケースで問題になることはありませんが、Androidのような制約のある環境で作業していて、これらの合成メソッドを多数生成している場合は、問題が発生する可能性があります。
  2. このフィールドへのアクセスは、直接ではなく、合成メソッドを介して間接的に行われます。パフォーマンスに非常に敏感なユースケースを除いて、これも問題にはなりません。パフォーマンス上の理由からここでgetterメソッドを使用したくない場合は、合成getterメソッドも必要ありません。これが実際に問題になることはめったにありません。

要するに、彼らは本当に悪くはありません。合成メソッドを回避する具体的な理由がない限り(つまり、それらがアプリケーションのボトルネックであると最終的に判断した場合)、コンパイラに適切と思われる方法で生成させる必要があります。気になる場合は、Eclipseの警告をオフにすることを検討してください。

私はそれらについて何をすべきですか?

コンパイラが合成メソッドを生成しないようにしたい場合は、いくつかのオプションがあります。

オプション1:権限を変更する

Package-privateまたはprotectedfieldsは、内部クラスに直接アクセスできます。特にSwingアプリケーションのようなものの場合、これは問題ないはずです。しかし、あなたはこれを避けたいと言っているので、私たちは行きます。

オプション2:ゲッターを作成する

フィールドはそのままにしておきますが、明示的にprotectedまたはpublicゲッターを作成し、代わりにそれを使用します。これは基本的にコンパイラが自動的に行うことですが、メソッドの動作を直接制御できるようになりました。

オプション3:ローカル変数を使用し、参照を両方のクラスと共有します

これはより多くのコードですが、内部クラスと外部クラスの関係を明示的にしているので、私の個人的なお気に入りです。

public Synthetic() {
  // Create final local instance - will be reachable by the inner class
  final JButton testButton = new JButton("Run");
  testButton.addActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          /* Sample code */
          if( testButton.getText().equals("Pause") ) {
            resetButton();
          } else if( testButton.getText().equals("Run") ) {
            testButton.setText("Pause");
          }
        }
      });
  // associate same instance with outer class - this.testButton can be final too
  this.testButton = testButton;
}

これは必ずしも実際にやりたいことではありません。たとえばtestButton、後で別のオブジェクトを指すように変更できる場合は、再構築する必要がありますActionListener(ただし、これもより明確であるため、おそらくこれは機能です)が、その意図を最も明確に示すオプションだと思います。


スレッドセーフは別として

サンプルTestクラスはスレッドセーフではありません-testString別のクラスに設定されてThreadいますが、その割り当てで同期していません。testStringすべてのスレッドが更新を確実に認識できるようにするには、としてマークを付けるvolatileだけで十分です。はコンストラクターでのみ設定されるため、この例ではこの問題は発生しませんが、その場合は、としてマークすることをお勧めSyntheticします。testButtontestButtonfinal

于 2013-04-26T00:23:19.787 に答える
3

testButton2番目の例では、直接アクセスする必要はありません。アクションイベントのソースを取得することでアクセスできます。

メソッドの場合、resetButton()アクションするオブジェクトを渡すための引数を追加できます。これを行った場合、アクセス制限を下げることはそれほど大きな問題ではありません。

于 2010-12-21T17:04:43.813 に答える
0

コンテキスト(かなりコストのかかる操作の一部として変数に一度割り当てる)を考えると、何もする必要はないと思います。

于 2010-12-21T16:16:22.653 に答える
0

問題は、親クラスに文字列を設定していることだと思います。スレッドはその変数が再びどこにあるかを確認する必要があるため、これはパフォーマンスを低下させます。よりクリーンなアプローチは、文字列を返すCallableを使用してから、.get()または結果を返す何かを実行することだと思います。結果を取得したら、データを親クラスに戻すことができます。

アイデアは、他のクラスに変数を設定する代わりに、スレッドが1つのことと1つのことだけを実行するようにすることです。これはよりクリーンなアプローチであり、内側のスレッドはそれ自体の外側にアクセスしないため、おそらくより高速です。これは、ロックが少ないことを意味します。:)

于 2010-12-21T16:27:35.830 に答える
0

これは、Javaのデフォルトの可視性(「パッケージプライベート」とも呼ばれる)が使用されるまれなケースの1つです。

public class Test {
    /* no private */ String testString;

    public void performAction() {

        new Thread( new Runnable() {
            @Override
            public void run() {
                testString = "initialize"; // **
            }
        });
    }
}

これにより、次のことが行われます。

  • testStringは、外部クラス()と同じパッケージ内のすべてのクラスで使用できるようになりましたTest
  • 内部クラスは実際にはとして生成されるためOuterClassPackage.OuterClassName$InnerClassName、それらも同じパッケージに存在します。したがって、このフィールドに直接アクセスできます。
  • このフィールドを作成するのとは対照的にprotected、デフォルトの可視性では、サブクラスがこのフィールドを使用できるようにはなりません(もちろん同じパッケージにある場合を除く)。したがって、外部ユーザーのためにAPIを汚染することはありません。

を使用する場合private、javacは代わりに合成アクセサーを生成します。これ自体はJavaのデフォルトの可視性を備えた単なるゲッターメソッドです。したがって、追加のメソッドのオーバーヘッドを最小限に抑えることを除いて、基本的に同じことを行います。

于 2016-02-28T21:06:27.590 に答える