これは一般的で些細なことかもしれませんが、具体的な答えを見つけるのに苦労しているようです。 C#にはデリゲートの概念があり、これはC++の関数ポインターの概念に強く関連しています。 Javaに同様の機能はありますか?ポインターがやや欠けていることを考えると、これに関する最善の方法は何ですか?そして、明確にするために、我々はここでファーストクラスを話している。
関数ポインターのような機能の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);
インターフェイスで関数ポインターを置き換えることができます。コレクションを実行して、各要素で何かをしたいとしましょう。
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++;
}
});
リフレクションを使用してそれを行うことができます。
パラメータとしてオブジェクトとメソッド名を(文字列として)渡し、メソッドを呼び出します。例えば:
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");
もちろん、すべての例外を確認し、必要なキャストを追加します。
いいえ、関数はJavaのファーストクラスオブジェクトではありません。ハンドラークラスを実装することで同じことを行うことができます-これはSwingなどでコールバックを実装する方法です。
しかし、将来のバージョンのJavaにはクロージャー(あなたが話していることの正式名)の提案があります- Javaworld には興味深い記事があります。
これは、Steve Yeggeの 名詞王国での実行 を思い起こさせます。基本的に、Javaはすべてのアクションにオブジェクトを必要とするため、関数ポインターのような「動詞のみ」のエンティティはありません。
同様の機能を実現するには、匿名の内部クラスを使用できます。
インターフェイス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.
}
}
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
}
}
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");
}
}
}
リフレクションを使用して、Javaにコールバック/デリゲートのサポートを実装しました。詳細と作業ソースは 私のWebサイトで入手可能 です。
仕組み
WithParmsという名前のネストされたクラスを持つCallbackという名前のプリンシパルクラスがあります。コールバックを必要とするAPIは、パラメーターとしてCallbackオブジェクトを受け取り、必要であれば、メソッド変数としてCallback.WithParmsを作成します。このオブジェクトのアプリケーションの多くは再帰的であるため、これは非常にきれいに機能します。
パフォーマンスが依然として優先事項であるため、すべての呼び出しのパラメーターを保持するスローアウェイオブジェクト配列を作成する必要はありませんでした。シナリオでは、毎秒数千のデータ構造を処理することになります。
スレッドセーフであるためには、APIメソッドの呼び出しごとにパラメーター配列が一意に存在する必要があり、効率のために、コールバックの呼び出しごとに同じ配列を使用する必要があります。コールバックを呼び出し用のパラメーター配列にバインドするために、作成が安価な2番目のオブジェクトが必要でした。しかし、いくつかのシナリオでは、呼び出し側は他の理由ですでにパラメーター配列を持っています。これら2つの理由により、パラメーター配列はCallbackオブジェクトに属していませんでした。また、呼び出しの選択(パラメーターを配列または個々のオブジェクトとして渡す)は、コールバックを使用するAPIの手に属し、内部の動作に最適な呼び出しを使用できるようにします。
次に、WithParmsネストクラスはオプションであり、2つの目的を果たします。コールバック呼び出しに必要なパラメーターオブジェクト配列を含み、パラメーター配列をロードする10のオーバーロードinvoke()メソッド(1〜10パラメーター)を提供します。コールバックターゲットを呼び出します。
Lambdajライブラリーでクロージャーがどのように実装されているかを確認してください。実際には、C#デリゲートに非常によく似た動作があります。
ここにいるほとんどの人に比べて、私は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();