約10行のコードを使用するメソッドがあります。コードの1行を変更する小さな計算を除いて、まったく同じことを行うメソッドをさらに作成したいと思います。これは関数ポインターを渡してその1行を置き換えるのに最適なアプリケーションですが、Javaには関数ポインターがありません。私の最善の選択肢は何ですか?
::
約10行のコードを使用するメソッドがあります。コードの1行を変更する小さな計算を除いて、まったく同じことを行うメソッドをさらに作成したいと思います。これは関数ポインターを渡してその1行を置き換えるのに最適なアプリケーションですが、Javaには関数ポインターがありません。私の最善の選択肢は何ですか?
::
回答:
匿名の内部クラス
String
たとえば、を返すparamを指定して関数を渡したいとしますint
。
既存のインターフェイスを再利用できない場合は、最初に関数を唯一のメンバーとして持つインターフェイスを定義する必要があります。
interface StringFunction {
int func(String param);
}
ポインタを受け取るメソッドは、次のようにStringFunction
インスタンスを受け入れるだけです。
public void takingMethod(StringFunction sf) {
int i = sf.func("my string");
// do whatever ...
}
そして、そのように呼ばれます:
ref.takingMethod(new StringFunction() {
public int func(String param) {
// body
}
});
編集: Java 8では、ラムダ式で呼び出すことができます:
ref.takingMethod(param -> bodyExpression);
定義済みの数の異なる計算がその1行で実行できる場合、列挙型を使用すると、戦略パターンを実装するための迅速かつ明確な方法になります。
public enum Operation {
PLUS {
public double calc(double a, double b) {
return a + b;
}
},
TIMES {
public double calc(double a, double b) {
return a * b;
}
}
...
public abstract double calc(double a, double b);
}
明らかに、戦略メソッド宣言と各実装の正確に1つのインスタンスはすべて、単一のクラス/ファイルで定義されます。
渡したい関数を提供するインターフェースを作成する必要があります。例えば:
/**
* A simple interface to wrap up a function of one argument.
*
* @author rcreswick
*
*/
public interface Function1<S, T> {
/**
* Evaluates this function on it's arguments.
*
* @param a The first argument.
* @return The result.
*/
public S eval(T a);
}
次に、関数を渡す必要がある場合は、そのインターフェースを実装できます。
List<Integer> result = CollectionUtilities.map(list,
new Function1<Integer, Integer>() {
@Override
public Integer eval(Integer a) {
return a * a;
}
});
最後に、map関数は渡されたFunction1を次のように使用します。
public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn,
Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
Set<K> keySet = new HashSet<K>();
keySet.addAll(m1.keySet());
keySet.addAll(m2.keySet());
results.clear();
for (K key : keySet) {
results.put(key, fn.eval(m1.get(key), m2.get(key)));
}
return results;
}
多くの場合、パラメーターを渡す必要がない場合は、独自のインターフェースの代わりにRunnableを使用できます。または、他のさまざまな手法を使用して、パラメーターカウントの「固定」を少なくすることもできますが、これは通常、タイプセーフティとのトレードオフです。(または、関数オブジェクトのコンストラクターをオーバーライドして、その方法でparamsを渡すことができます。多くのアプローチがあり、いくつかは特定の状況でよりよく機能します。)
::
演算子を使用したメソッド参照メソッドが関数インターフェイスを受け入れる場合、メソッド引数でメソッド参照を使用できます。機能インターフェースは、1つの抽象メソッドのみを含むインターフェースです。(機能インターフェースには、1つ以上のデフォルトのメソッドまたは静的メソッドが含まれる場合があります。)
IntBinaryOperator
機能的なインターフェースです。その抽象メソッドはapplyAsInt
、2つint
のをパラメーターとして受け入れ、を返しますint
。Math.max
また、2つint
のを受け入れ、を返しますint
。この例でA.method(Math::max);
は、parameter.applyAsInt
makeは2つの入力値をに送信し、そのMath.max
結果を返しMath.max
ます。
import java.util.function.IntBinaryOperator;
class A {
static void method(IntBinaryOperator parameter) {
int i = parameter.applyAsInt(7315, 89163);
System.out.println(i);
}
}
import java.lang.Math;
class B {
public static void main(String[] args) {
A.method(Math::max);
}
}
一般的に、次のものを使用できます。
method1(Class1::method2);
の代わりに:
method1((arg1, arg2) -> Class1.method2(arg1, arg2));
これは以下の略です:
method1(new Interface1() {
int method1(int arg1, int arg2) {
return Class1.method2(arg1, agr2);
}
});
これを行うこともできます(一部のRAREの場合、これは理にかなっています)。問題(そしてそれは大きな問題です)は、クラス/インターフェースを使用することのすべての型保証が失われ、メソッドが存在しない場合に対処しなければならないことです。
これには、アクセス制限を無視してプライベートメソッドを呼び出すことができる「利点」があります(例には示されていませんが、コンパイラーが通常は呼び出せないメソッドを呼び出すことができます)。
繰り返しになりますが、これが理にかなっているのはまれなケースですが、そのような場合に備えておくと便利です。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final String methodName;
final Method method;
final Main main;
main = new Main();
if(argv.length == 0)
{
methodName = "foo";
}
else
{
methodName = "bar";
}
method = Main.class.getDeclaredMethod(methodName, int.class);
main.car(method, 42);
}
private void foo(final int x)
{
System.out.println("foo: " + x);
}
private void bar(final int x)
{
System.out.println("bar: " + x);
}
private void car(final Method method,
final int val)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
method.invoke(this, val);
}
}
異なる行が1つしかない場合は、フラグなどのパラメーターと、いずれかの行を呼び出すif(flag)ステートメントを追加できます。
また、Java 7で行われている、クロージャーに関する作業について聞いてみることもできます。
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
演算子を使用した新しいJava 8 機能インターフェースとメソッド参照::
。
Java 8は、 " @ Functional Interface "ポインターを使用してメソッド参照(MyClass :: new)を維持できます。同じメソッド名は必要なく、同じメソッドシグネチャのみが必要です。
例:
@FunctionalInterface
interface CallbackHandler{
public void onClick();
}
public class MyClass{
public void doClick1(){System.out.println("doClick1");;}
public void doClick2(){System.out.println("doClick2");}
public CallbackHandler mClickListener = this::doClick;
public static void main(String[] args) {
MyClass myObjectInstance = new MyClass();
CallbackHandler pointer = myObjectInstance::doClick1;
Runnable pointer2 = myObjectInstance::doClick2;
pointer.onClick();
pointer2.run();
}
}
それで、ここには何がありますか?
あなたはリスナーのためだけに、そしてそれのためだけに機能的なインターフェースを使用すべきです!
他のすべてのそのような関数ポインターは、コードの可読性と理解能力にとって本当に悪いからです。ただし、直接メソッド参照は、たとえばforeachなどの場合に便利です。
いくつかの事前定義された機能インターフェースがあります。
Runnable -> void run( );
Supplier<T> -> T get( );
Consumer<T> -> void accept(T);
Predicate<T> -> boolean test(T);
UnaryOperator<T> -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R> -> R apply(T);
BiFunction<T,U,R> -> R apply(T, U);
//... and some more of it ...
Callable<V> -> V call() throws Exception;
Readable -> int read(CharBuffer) throws IOException;
AutoCloseable -> void close() throws Exception;
Iterable<T> -> Iterator<T> iterator();
Comparable<T> -> int compareTo(T);
Comparator<T> -> int compare(T,T);
以前のJavaバージョンでは、Adrian Petrescuが前述したように、同様の機能と構文を備えたGuava Librariesを試す必要があります。
追加の調査については、Java 8チートシートをご覧ください。
そして、Java言語仕様§15.13リンクのための帽子を持つ男に感謝します。
@sblundyの答えは素晴らしいですが、匿名の内部クラスには2つの小さな欠陥があります。主な理由は、再利用できない傾向があり、2番目はかさばる構文です。
良い点は、彼のパターンがメインクラス(計算を実行するクラス)を変更せずに完全なクラスに拡張されることです。
新しいクラスをインスタンス化するときに、方程式の定数として機能できるパラメーターをそのクラスに渡すことができます。つまり、内部クラスの1つが次のようになっている場合です。
f(x,y)=x*y
しかし時々あなたはそれが必要です:
f(x,y)=x*y*2
そしておそらく3分の1は:
f(x,y)=x*y/2
2つの匿名内部クラスを作成したり、「パススルー」パラメーターを追加する代わりに、次のようにインスタンス化する単一のACTUALクラスを作成できます。
InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);
クラスに定数を格納し、インターフェイスで指定されたメソッドで使用するだけです。
実際、関数が保存/再利用されないことがわかっている場合は、次のようにすることができます。
InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);
しかし、不変クラスの方が安全です。このようなクラスを変更可能にする理由を考え出すことはできません。
匿名の内部クラスを聞くたびにうんざりするので、私は本当にこれだけを投稿します。プログラマーが最初に行ったのは、実際のクラスを使用する必要があるときに匿名にしたため、「必須」である冗長なコードをたくさん見ました彼の決定を再考した。
Javaでプログラミングするときに私が本当に見逃しているものの1つは、関数のコールバックです。これらが常に表示される必要がある状況の1つは、階層ごとに再帰的に処理する階層で、各アイテムに対して特定のアクションを実行したい場合です。ディレクトリツリーを歩く、またはデータ構造を処理するようなものです。私の中のミニマリストは、特定のケースごとにインターフェースを定義してから実装を定義する必要があることを嫌っています。
ある日、なぜだろうと思いました。メソッドポインター-メソッドオブジェクトがあります。JITコンパイラーを最適化することで、リフレクティブな呼び出しがパフォーマンスに大きな影響を与えることはもうありません。そして、たとえば、ある場所から別の場所にファイルをコピーする以外に、反映されたメソッド呼び出しのコストは取るに足らないものになります。
それについてもっと考えたとき、OOPパラダイムでのコールバックにはオブジェクトとメソッドを一緒にバインドする必要があることに気付きました-Callbackオブジェクトを入力してください。
Javaのコールバックのリフレクションベースのソリューションを確認してください。使用は無料です。
OK、このスレッドはすでに古くなっているので、おそらく私の答えは質問には役に立たないでしょう。しかし、このスレッドは私の解決策を見つけるのに役立ちましたので、とにかくここに公開します。
既知の入力と既知の出力(両方ともdouble)を持つ可変静的メソッドを使用する必要がありました。したがって、メソッドのパッケージと名前を知っているので、次のように作業できます。
java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);
1つのdoubleをパラメーターとして受け入れる関数の場合。
したがって、私の具体的な状況では、
java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);
後でより複雑な状況でそれを呼び出しました
return (java.lang.Double)this.Function.invoke(null, args);
java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);
ここで、アクティビティは任意のdouble値です。SoftwareMonkeyが行ったように、これをもう少し抽象化して一般化することを考えていますが、現在はそれで十分満足しています。3行のコード。クラスやインターフェースは必要ありません。それほど悪くはありません。
code
関数の配列のインターフェースなしで同じことを行うには:
class NameFuncPair
{
public String name; // name each func
void f(String x) {} // stub gets overridden
public NameFuncPair(String myName) { this.name = myName; }
}
public class ArrayOfFunctions
{
public static void main(String[] args)
{
final A a = new A();
final B b = new B();
NameFuncPair[] fArray = new NameFuncPair[]
{
new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
};
// Go through the whole func list and run the func named "B"
for (NameFuncPair fInstance : fArray)
{
if (fInstance.name.equals("B"))
{
fInstance.f(fInstance.name + "(some args)");
}
}
}
}
class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }
ラムダージをチェック
http://code.google.com/p/lambdaj/
特にその新しい閉鎖機能
http://code.google.com/p/lambdaj/wiki/Closures
そして、意味のないインターフェイスを作成したり、醜い内部クラスを使用したりせずに、クロージャまたは関数ポインタを定義する非常に読みやすい方法を見つけるでしょう。
うわー、私がすでにJavaに対して行ったことを考えると、それほど難しくないDelegateクラスを作成し、それを使用してTが戻り値の型であるパラメーターを渡すのではないでしょうか。申し訳ありませんが、C ++ / C#プログラマーとして一般的にJavaを習得しているだけなので、非常に便利な関数ポインターが必要です。メソッド情報を扱うクラスに精通している場合は、それを行うことができます。javaライブラリでは、java.lang.reflect.methodになります。
常にインターフェースを使用する場合は、常にそれを実装する必要があります。イベント処理では、ハンドラーのリストへの登録/登録解除を行うのに最適な方法はありませんが、値型ではなく関数を渡す必要があるデリゲートの場合は、デリゲートクラスを作成して、インターフェースをアウトクラスで処理します。
誰かがその振る舞いを定義するためにパラメータの1つのセットを受け取る関数を渡すのに苦労しているが、Schemeのように実行する別のパラメータのセット:
(define (function scalar1 scalar2)
(lambda (x) (* x scalar1 scalar2)))
Javaでのパラメーター定義の動作を伴うPass関数を参照してください。
Java8以降、ラムダを使用できます。ラムダには、公式のSE 8 APIにライブラリがあります。
使用法: 抽象メソッドを1つだけ持つインターフェースを使用する必要があります。そのインスタンスを作成します(すでに提供されている1つのJava SE 8を使用することもできます)。
Function<InputType, OutputType> functionname = (inputvariablename) {
...
return outputinstance;
}
詳細については、ドキュメントをチェックアウトしてください:https : //docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
Java 8より前は、関数ポインターのような機能の最も近い代替物は匿名クラスでした。例えば:
Collections.sort(list, new Comparator<CustomClass>(){
public int compare(CustomClass a, CustomClass b)
{
// Logic to compare objects of class CustomClass which returns int as per contract.
}
});
しかし、現在Java 8 では、ラムダ式と呼ばれる非常に優れた代替手段があり、次のように使用できます。
list.sort((a, b) -> { a.isBiggerThan(b) } );
ここで、isBiggerThanはのメソッドですCustomClass
。ここでメソッド参照を使用することもできます。
list.sort(MyClass::isBiggerThan);