2009-05-01 2 views
4

「コード・コンプリート」という本では、プログラミング言語に翻訳(言語でプログラミングするのではなく)しています。彼は、選択したプログラミング言語の制限によって自分自身を制限するべきではないということを意味します。言語へのプログラミング:Javaでコールバックを定義する最もエレガントな方法は何ですか?

コールバックはよく使用される機能です。私は興味があります:プログラマのコールバックをJava言語に変換する最もエレガントな方法は何ですか?

+0

[んJavaはクロージャを必要とする?](http://stackoverflow.com/questions/50255/does-java-need-closures) –

答えて

1

この記事をチェックアウト:

http://www.onjava.com/pub/a/onjava/2003/05/21/delegates.html

コールバックは基本的に特殊なケースですが(C#は、それらを持っているとして)デリゲートをOD、と記事はJavaで、C#のデリゲートに似た何かの実装を提供します。

+0

それは言語に非常に多くのプログラミングです。実際にはリフレクションを使用しますが、シンプルなクラス(デレゲータ)ではリフレクションは隠され、提示されるインターフェイスは匿名クラスよりもはるかに優れています。素晴らしい発見。 – Mnementh

2

残念ながら、Javaでは、関数はファーストクラスのオブジェクトではありません。あなたができる最善のインターフェイスを使用することです:

public interface MyCallback 
{ 
    public void theCallback(int arg); 
} 

public class Sample 
{ 
    public static void takesACallback(MyCallback callback) 
    { 
     ... 
     callback.theCallback(arg); 
    } 
} 

public class Sample2 
{ 
    public static void main(String[] args) 
    { 
     Sample.takesACallback(new MyCallback() 
     { 
      void theCallback(int arg) 
      { 
       // do a little dance 
      } 
     }); 
    } 
} 
+3

ええ、私は考えていません「エレガント」と「コールバック」はJavaで一緒になります。 –

+6

"エレガント"と "Java"は一緒に行かないと言います。 –

4

私はJavaで関数ポインタ/代表者の不在を回避するために見た中で最も一般的な方法は、ファンクタを使用することです。

基本的には、単一の方法でインタフェースを定義し、あなたのコールバックとしてのインスタンスを使用します。

public interface Callback<T,V>{ 
    public T invoke(V context); 
} 

たくさんより詳細なC/C++やC#の同等物よりも、その、それが動作します。標準ライブラリのこのパターンの例は、Comparatorインターフェイスです。

+1

インターフェイス名の前に "I"を置かないでください。 –

+0

私は最近掘り下げてきたすべてのwin32から逃げ出した。 –

+0

コールバックには意味のある名前を付けてください.FooCallbackのようにコールバック自体はあまりにも一般的で、システム内の他のコンポーネントとの関係を理解するのに役立ちません。 –

6

Javaは、あらゆる種類の状況であらゆる種類のコールバックを使用します。 AWTリスナーの最も古くからの昔から、Javaはコールバックに関するものです。

Javaコールバックには2つの基本的な「味」があります。

public class MyThing implements StateChangeListener { 

    //this method is declared in StateChangeListener 
    public void stateChanged() { 
     System.out.println("Callback called!"); 
    } 

    public MyThing() { 
     //Here we declare ourselves as a listener, which will eventually 
     //lead to the stateChanged method being called. 
     SomeLibraryICareAbout.addListener(this); 
    } 
} 

のJavaコールバックの第2の香味は、匿名の内部クラスである:最初のインターフェイスを実装する方法であって、

public class MyThing { 

    public MyThing() { 
     //Here we declare ourselves as a listener, which will eventually 
     //lead to the stateChanged method being called. 
     SomeLibraryICareAbout.addListener(new StateChangeListener() { 
      //this method is declared in StateChangeListener 
      public void stateChanged() { 
       System.out.println("Callback called!"); 
      } 
     }); 
    } 
} 

他の方法は、別個のevent-を使用して、リフレクションを使用することを含む、あまりにも、ありますハンドリングクラス、およびアダプターパターン。

+0

+1の匿名の内部クラス –

+1

匿名の名前付きリスナーは、インターフェイスの具体的な実装である同じものです。 –

0

非常に一般的なコールバック構築は、ActionListenersがおそらく最も把握しやすいSwingのイベントハンドラです。

は、あなたは非常に頻繁に

あなたは、適切なスイング法にリスナーを渡す
listener = new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
    // do stuff... 
    } 
}; 

に似た適切なインタフェースを実装する匿名クラスのインスタンスを提供http://java.sun.com/docs/books/tutorial/uiswing/events/actionlistener.html

を見てください。

0

私は個人的にはJavaが何らかの形のクロージャーサポートを必死に必要と感じています。その間、Javaでリフレクションベースの汎用メソッドコールバックを実装しました。それはposted on my websiteです。

このアプローチの利点は一般性です。その目的は、毎回インターフェイスを定義することなく、代わりにAPIを使用してコードを実行して作業を実行することなく、ファイルシステムツリーのようなAPIを書くことでした。各ファイルを処理するためにファイル・システム・ツリーウォーキング例えば

プロセスディレクトリツリーAPI

/** 
* Process a directory using callbacks. To interrupt, the callback must throw an (unchecked) exception. 
* Subdirectories are processed only if the selector is null or selects the directories, and are done 
* after the files in any given directory. When the callback is invoked for a directory, the file 
* argument is null; 
* <p> 
* The callback signature is: 
* <pre> void callback(File dir, File ent);</pre> 
* <p> 
* @return   The number of files processed. 
*/ 
static public int processDirectory(File dir, Callback cbk, FileSelector sel) { 
    return _processDirectory(dir,new Callback.WithParms(cbk,2),sel); 
    } 

static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) { 
    int         cnt=0; 

    if(!dir.isDirectory()) { 
     if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; } 
     } 
    else { 
     cbk.invoke(dir,(Object[])null); 

     File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel)); 
     if(lst!=null) { 
      for(int xa=0; xa<lst.length; xa++) { 
       File ent=lst[xa]; 
       if(!ent.isDirectory()) { 
        cbk.invoke(dir,ent); 
        lst[xa]=null; 
        cnt++; 
        } 
       } 
      for(int xa=0; xa<lst.length; xa++) { 
       File ent=lst[xa]; 
       if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); } 
       } 
      } 
     } 
    return cnt; 
    } 

上記方法によりプロセスディレクトリAPI

を使用私は現在、どの操作でもディレクトリツリーを非常に簡単に処理できます。ファイル/ツリーの削除などの降順操作の前後の両方でディレクトリ上のコールバックを呼び出すマイナーな変更を行うこともできます(呼び出しの前/後の性質を示す追加のパラメータが必要です) )。この端を発する関連する質問は以下のようになり

static private final Method    COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class); 

... 

IoUtil.processDirectory(root,new Callback(this,COUNT),selector); 

... 

private void callback_count(File dir, File fil) { 
    if(fil!=null) {                // file is null for processing a directory 
     fileTotal++; 
     if(fil.length()>fileSizeLimit) { 
      throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil); 
      } 
     } 
    progress("Counting",dir,fileTotal); 
    } 
関連する問題