これは一般的で些細なことかもしれませんが、具体的な答えを見つけるのに苦労しているようです。C#にはデリゲートの概念があり、これはC ++からの関数ポインターの概念に強く関連しています。Javaに同様の機能はありますか?ポインタがいくらか欠けていることを考えると、これについての最良の方法は何ですか?明確にするために、ここではファーストクラスについて話します。
これは一般的で些細なことかもしれませんが、具体的な答えを見つけるのに苦労しているようです。C#にはデリゲートの概念があり、これはC ++からの関数ポインターの概念に強く関連しています。Javaに同様の機能はありますか?ポインタがいくらか欠けていることを考えると、これについての最良の方法は何ですか?明確にするために、ここではファーストクラスについて話します。
回答:
関数ポインタのような機能のJavaイディオムは、インターフェースを実装する匿名クラスです。
Collections.sort(list, new Comparator<MyClass>(){
public int compare(MyClass a, MyClass b)
{
// compare objects
}
});
更新:上記はJava 8より前のバージョンのJavaで必要です。これで、より優れた代替手段、つまりラムダがあります。
list.sort((a, b) -> a.isGreaterThan(b));
およびメソッド参照:
list.sort(MyClass::isGreaterThan);
std::bind
パラメータを関数にバインドし、呼び出し可能なオブジェクトを返すも備えています。これらの理由でCを防御することはできませんが、C ++ は Javaよりも優れています。
インターフェイスを使用して関数ポインターを置き換えることができます。コレクションを実行して、各要素で何かをしたいとしましょう。
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には興味深い記事があります。
これは、名詞の王国におけるスティーブ・イェッジの処刑を思い起こさせます。基本的に、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はラムダとメソッド参照を導入しました。したがって、関数が関数インターフェイスと一致する場合(独自の関数を作成できます)、この場合はメソッド参照を使用できます。
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
}
}
alias
ますか:例 public List<T> collect(T t) = Collections::collect
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でコールバック/デリゲートサポートを実装しました。詳細と作業ソースは私のウェブサイトで入手できます。
使い方
WithParmsという名前のネストされたクラスを持つCallbackという名前の主要なクラスがあります。コールバックを必要とするAPIは、パラメータとしてCallbackオブジェクトを受け取り、必要に応じて、メソッド変数としてCallback.WithParmsを作成します。このオブジェクトのアプリケーションの多くは再帰的であるため、これは非常にきれいに機能します。
パフォーマンスは依然として私にとって優先度が高いので、すべての呼び出しのパラメーターを保持する使い捨てオブジェクト配列を作成する必要はありませんでした。結局、大きなデータ構造では、何千もの要素があり、メッセージ処理でもあります。シナリオでは、1秒間に何千ものデータ構造を処理することになります。
スレッドセーフになるためには、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();