web-dev-qa-db-ja.com

Javaの関数ポインター

これは一般的で些細なことかもしれませんが、具体的な答えを見つけるのに苦労しているようです。 C#にはデリゲートの概念があり、これはC++の関数ポインターの概念に強く関連しています。 Javaに同様の機能はありますか?ポインターがやや欠けていることを考えると、これに関する最善の方法は何ですか?そして、明確にするために、我々はここでファーストクラスを話している。

147
Ben Lakey

関数ポインターのような機能のJavaイディオムは、インターフェースを実装する匿名クラスです。

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

更新: Javaより前のJavaバージョンでは上記が必要です。

list.sort((a, b) -> a.isGreaterThan(b));

およびメソッド参照:

list.sort(MyClass::isGreaterThan);
125

インターフェイスで関数ポインターを置き換えることができます。コレクションを実行して、各要素で何かをしたいとしましょう。

public interface IFunction {
  public void execute(Object o);
}

これは、CollectionUtils2.doFunc(Collection c、IFunction f)などに渡すことができるインターフェイスです。

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

例として、数字のコレクションがあり、すべての要素に1を追加するとします。

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
63
raupach

リフレクションを使用してそれを行うことができます。

パラメータとしてオブジェクトとメソッド名を(文字列として)渡し、メソッドを呼び出します。例えば:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

そして、次のように使用します:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

もちろん、すべての例外を確認し、必要なキャストを追加します。

41
zoquete

いいえ、関数はJavaのファーストクラスオブジェクトではありません。ハンドラークラスを実装することで同じことを行うことができます-これはSwingなどでコールバックを実装する方法です。

しかし、将来のバージョンのJavaにはクロージャー(あなたが話していることの正式名)の提案があります- Javaworld には興味深い記事があります。

20
jwoolard

これは、Steve Yeggeの 名詞王国での実行 を思い起こさせます。基本的に、Javaはすべてのアクションにオブジェクトを必要とするため、関数ポインターのような「動詞のみ」のエンティティはありません。

15
Yuval F

同様の機能を実現するには、匿名の内部クラスを使用できます。

インターフェイスFooを定義する場合:

interface Foo {
    Object myFunc(Object arg);
}

引数として「関数ポインタ」を受け取るメソッドbarを作成します。

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

最後に、次のようにメソッドを呼び出します。

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
8
Kingamajick

Java8では、ラムダと メソッド参照 が導入されています。したがって、関数が functional interface (独自に作成できます)に一致する場合、この場合はメソッド参照を使用できます。

Javaは、一連の 共通機能インターフェース を提供します。一方、次のことができます。

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
6
Alex

Javaにはそのようなものはありません。関数をオブジェクトにラップし、そのオブジェクトのメソッドに参照を渡すために、そのオブジェクトに参照を渡す必要があります。

構文的には、インプレースで定義された匿名クラスまたはクラスのメンバー変数として定義された匿名クラスを使用することで、これをある程度緩和できます。

例:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}
5
VoidPointer

リフレクションを使用して、Javaにコールバック/デリゲートのサポートを実装しました。詳細と作業ソースは 私のWebサイトで入手可能 です。

仕組み

WithParmsという名前のネストされたクラスを持つCallbackという名前のプリンシパルクラスがあります。コールバックを必要とするAPIは、パラメーターとしてCallbackオブジェクトを受け取り、必要であれば、メソッド変数としてCallback.WithParmsを作成します。このオブジェクトのアプリケーションの多くは再帰的であるため、これは非常にきれいに機能します。

パフォーマンスが依然として優先事項であるため、すべての呼び出しのパラメーターを保持するスローアウェイオブジェクト配列を作成する必要はありませんでした。シナリオでは、毎秒数千のデータ構造を処理することになります。

スレッドセーフであるためには、APIメソッドの呼び出しごとにパラメーター配列が一意に存在する必要があり、効率のために、コールバックの呼び出しごとに同じ配列を使用する必要があります。コールバックを呼び出し用のパラメーター配列にバインドするために、作成が安価な2番目のオブジェクトが必要でした。しかし、いくつかのシナリオでは、呼び出し側は他の理由ですでにパラメーター配列を持っています。これら2つの理由により、パラメーター配列はCallbackオブジェクトに属していませんでした。また、呼び出しの選択(パラメーターを配列または個々のオブジェクトとして渡す)は、コールバックを使用するAPIの手に属し、内部の動作に最適な呼び出しを使用できるようにします。

次に、WithParmsネストクラスはオプションであり、2つの目的を果たします。コールバック呼び出しに必要なパラメーターオブジェクト配列を含み、パラメーター配列をロードする10のオーバーロードinvoke()メソッド(1〜10パラメーター)を提供します。コールバックターゲットを呼び出します。

3
Lawrence Dol

Lambdajライブラリーでクロージャーがどのように実装されているかを確認してください。実際には、C#デリゲートに非常によく似た動作があります。

http://code.google.com/p/lambdaj/wiki/Closures

2
Mario Fusco

ここにいるほとんどの人に比べて、私はJavaが初めてですが、同様の提案を見たことがないので、提案する別の選択肢があります。その良い習慣かどうかわからない、または以前に提案されても、私はそれを手に入れませんでした。自己記述的だと思うので気に入っています。

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

その後、アプリケーションに応じて、割り当てます

 functionpointer = new Function1();
 functionpointer = new Function2();

等.

で呼び出す

 functionpointer.execute();
0
ozgeneral