匿名の内部クラスは外のクラスを使うなぜfinal型を使うのか

5332 ワード

  • プログラム設計言語の理論上:ローカル内部クラス(すなわち、メソッドに定義された内部クラス)は、それ自体がメソッド内部(形式パラメータ定義またはメソッド体に現れる可能性がある)であるため、アクセスメソッドにおけるローカル変数(形式パラメータまたはローカル変数)は当然である.とても自然な
  • です
  • なぜJAVAにはfinal型のローカル変数しかアクセスできないという制限があるのですか?
  • JAVA言語のコンパイラの設計者はもちろん、ローカル内部クラスがアクセスできる方法のすべてのローカル変数(理論的には自然な要求であるため)を実現しているが、コンパイラ技術は実現できないか、コストが極めて高い.
  • 困難はどこですか?いったいどこが難しいの?ローカル変数のライフサイクルとローカル内部クラスのオブジェクトのライフサイクルの不一致!
  • メソッドfが呼び出され、その呼び出しスタックに変数iが生成され、ローカル内部クラスオブジェクトinner_が生成されるobjectは、この局所変数iにアクセスする.メソッドf()の動作が終了すると、局所変数iは既に死亡する、存在しない.ただし:ローカル内部クラスオブジェクトinner_objectは、メソッドf()の実行に伴って死亡することなく、オブジェクトを参照する人がいない場合にのみ死亡する可能性がある.このとき:ローカル内部クラスオブジェクトinner_という「でたらめ」な結果が得られました.objectはすでに存在しないローカル変数iにアクセスします!
  • はどのようにして実現できますか?変数がfinalである場合、finalローカル変数を「コピー」することにより、レプリカは直接ローカル内部のデータメンバーとなる.このように、ローカル内部クラスがローカル変数にアクセスする場合、実際にアクセスするのは、このローカル変数の「レプリカ」(すなわち、このレプリカがそのローカル変数を表す)である.したがって、スタック内の真のローカル変数が死亡すると、ローカル内部クラスオブジェクトはローカル変数(実際には「レプリカ」にアクセス)にアクセスすることができ、ローカル変数の「ライフタイム」が延長されたような感じがする.

  • では、コアの問題は、「レプリカ」へのアクセスと、実際の元のローカル変数へのアクセスとの意味効果が同じになるようにするにはどうすればいいのでしょうか.変数がfinalの場合、基本データ型の場合、その値は変わらないため、その複製品は元の量と同じである.意味効果は同じです.(finalでない場合、コピーが元の変数と一致することは保証されません.なぜなら、メソッドでは元の変数に変更され、ローカル内部クラスではコピーに変更されます)変数がfinalの場合、参照タイプの場合、参照値が変わらない(すなわち、同じオブジェクトを永遠に指す)ため、コピーは元の参照変数と同じです.常に同じオブジェクト(finalであるため、このオブジェクトのみを指すことが保証され、他のオブジェクトを指すことができない)を指し、局所内部クラスでアクセスするレプリカとメソッドコードでアクセスする元のオブジェクトは、永遠に同じである、すなわち、意味効果は同じである.そうでない場合、メソッドで元の変数を変更し、ローカル内部クラスでコピーを変更する場合、コピーと元の変数が一致することは保証されません(したがって、元は同じ変数であるべきです).一言:この規定は仕方がない.プログラム設計言語の設計は実現技術の制限を受けることも説明する.これが一例です.なぜなら、私は多くの人がこのような観点を持っているのを見ています:設計と考えは最も重要で、実現の技術は重要ではありません.あなたが設計と規定をすれば、すべて実現することができます.
    次に、ABSClassを匿名で呼び出す方法の例を実装します.
        public static void test(final String s){
            ABSClass c = new ABSClass(){
               public void m(){
                  int x = s.hashCode();
                  System.out.println(x);
               }
            };
         //    .
        }

    コードから見ると、1つのメソッドの内部で定義された内部クラスのメソッドが外部メソッド内のローカル変数またはメソッドパラメータにアクセスするのは自然なことですが、内部クラスがコンパイルされるときにどのようにこの変数を取得するかは、内部クラスがそのライフサイクルを除いてメソッドの内部にあるため、他の面では一般的なクラスです.では、その外のローカル変数やメソッドパラメータはどのように内部クラスにアクセスされますか?コンパイラは実装時に実際には次のようになります.
    なぜ匿名の内部クラスとローカルの内部クラスはfinal変数にしかアクセスできないのか
    匿名の内部クラスがメソッドの内部に現れるため、変数の役割ドメインの問題です.このメソッドのパラメータまたはメソッドで定義された変数にアクセスする場合は、これらのパラメータと変数をfinalに修飾する必要があります.匿名の内部クラスはメソッドの内部にあるが、実際にコンパイルする場合、内部クラスはOuterにコンパイルされるからである.Inner,これは内部クラスの位置と外部クラスのメソッドが同じレベルにあることを示し,外部クラスのメソッドの変数またはパラメータはメソッドの局所変数にすぎず,これらの変数またはパラメータの役割ドメインはこのメソッドの内部でのみ有効である.コンパイル時に内部クラスとメソッドが同じレベルにあるため、メソッドの変数やパラメータはfinalであり、内部クラスのみ参照できます.
    Javaコード:
    public class MyClass {   
        public MyClass() {   
            final int finalValue = 10;   
            int not$Final = 20;   
            MyInterface myInterface = new MyInterface() {   
                public void functionWithoutPara() {   
                    //compile Error   
                    //System.out.println(noFinal);    
                    System.out.println(finalValue);   
                }   
      
                public void functionWithPara(int num) {   
                    System.out.println("The parameter " + num   
                            + " has been passed by the method");   
                }   
      
            };   
            myInterface.functionWithoutPara();   
            myInterface.functionWithPara(not$Final);   
            System.out.println(myInterface.getClass().getName());   
        }   
      
        public static void main(String[] args) {   
            new MyClass();   
      
        }   
      
    }

    二、なぜローカル内部クラスはfinal変数にしかアクセスできないのか
    簡単に言えば、役割ドメインの問題です.メソッドの外でやっていることは、メソッド内で定義された変数を変えることはできません.メソッドの中にこの時点でこの局所変数が存在しているかどうか分からないからです.この内部クラスではメソッド内のローカル変数は失効し,すなわち役割ドメイン内にないためアクセスできない.
    しかしなぜここはfinalでアクセスできるのでしょうか?Javaはcopy->local->variableという方法で実現されているため、finalと定義されたローカル変数をコピーして使用し、参照したものも持ってきて使用することができますが、再割り当てはできません.これによりaccess->local->variableという仮象ができますが、この場合は再付与できないため、予想できないことは起こりません
    三、ローカル内部クラスを定義し、ローカル内部クラスが外部で定義されたオブジェクトを使用している場合、コンパイラはパラメータ参照がfinalであることを要求するのはなぜですか.注:匿名の内部クラスを含むローカル内部クラス.
    理由は次のとおりです.
    abstract class ABSClass{
            public abstract void print();
    
            public class Test2{
                public static void test(final String s){//            ,    final
                    ABSClass c=new ABSClass(){
                         public void print(){
                               System.out.println(s);
                         }
                    };
                    c.print();
                }
                public static void main(String[] args){
                    test("Hello World!");
                }
            }
        }
  • JVMの各プロセスには複数のルートがあり、各static変数、メソッドパラメータ、ローカル変数はもちろん参照タイプを指す.ベースタイプはルートとすることができず、ルートは実は記憶アドレスである.ごみ回収器は、作業中にルートから参照されたオブジェクトを巡り、タグ付けします.これにより、最後尾に戻り、すべてのルートが遍歴した後、タグ付けされていないオブジェクトの説明が参照されません.すなわち、回収可能なオブジェクト(一部のオブジェクトにはfinalizedメソッドがある、参照されていないが、JVMにはfinalizedメソッドが実行されるまでそのキューから本当に参照されていないオブジェクトとして除去する専用のキューがある、回収可能である、これは本主題の議論に関係なく、世代の区分などを含めて後で説明する).これはよく見えます.
  • しかし、内部クラスのコールバックメソッドでは、sは静的変数でもメソッドの一時変数でもメソッドパラメータでもなく、ルートとして使用することはできません.内部クラスでも変数として参照することはできません.そのルートは内部クラスの外部にあるメソッドでは、外部変数sが他のオブジェクトに再指向されると、コールバックメソッドのこのオブジェクトsは参照を失います.回収される可能性があるが、内部クラスのコールバック方法の多くは他のスレッドで実行されるため、回収後もアクセスを継続する可能性がある.これはどんな結果になりますか?
  • でfinal修飾子を使用すると、オブジェクトの参照は変更されないだけでなく、コンパイラはコールバックメソッドにおけるオブジェクトのライフサイクルを維持し続ける.これがfinal変数とfinalパラメータの根本的な意味です