305

約 10 行のコードのメソッドがあります。コードの 1 行を変更する小さな計算を除いて、まったく同じことを行うメソッドをさらに作成したいと考えています。これは、関数ポインターを渡してその 1 行を置き換えるのに最適なアプリケーションですが、Java には関数ポインターがありません。私の最良の代替手段は何ですか?

4

22 に答える 22

272

匿名の内部クラス

Stringを返すパラメーターを使用して関数を渡したいとします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);
于 2008-09-23T17:21:43.927 に答える
32

「関数ポインター」ごとに、計算を実装する小さなファンクター クラスを作成します。すべてのクラスが実装するインターフェイスを定義し、それらのオブジェクトのインスタンスをより大きな関数に渡します。「指揮パターン」と「戦略パターン」の組み合わせです。

@sblundyの例は良いです。

于 2008-09-23T17:27:06.597 に答える
28

その 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 つのインスタンスはすべて、1 つのクラス/ファイルで定義されています。

于 2009-03-27T00:16:31.537 に答える
24

渡したい関数を提供するインターフェースを作成する必要があります。例えば:

/**
 * 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 を使用することがよくあります。または、他のさまざまな手法を使用して、パラメーター カウントの「固定」を少なくすることもできますが、通常はタイプ セーフとのトレードオフになります。(または、関数オブジェクトのコンストラクターをオーバーライドして、そのようにパラメーターを渡すことができます。多くのアプローチがあり、特定の状況ではより適切に機能するものもあります。)

于 2008-09-23T17:30:21.567 に答える
15

これを行うこともできます (まれに意味があります)。問題 (そしてそれは大きな問題です) は、クラス/インターフェイスを使用することのすべての型安全性を失い、メソッドが存在しない場合に対処しなければならないことです。

アクセス制限を無視してプライベート メソッドを呼び出すことができるという「利点」があります (例には示されていませんが、通常はコンパイラが呼び出しを許可しないメソッドを呼び出すことができます)。

繰り返しますが、これが理にかなっているケースはめったにありませんが、そのような場合に便利なツールです。

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);
    }
}
于 2009-03-26T21:39:48.387 に答える
13

異なる行が 1 つだけある場合は、フラグなどのパラメーターと、いずれかの行を呼び出す if(flag) ステートメントを追加できます。

于 2009-03-26T21:56:05.243 に答える
12

また、Java 7 でクロージャを含む作業が進行中であることに興味があるかもしれません。

Java のクロージャの現在の状態は?

http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures

于 2008-09-23T21:22:33.037 に答える
9

@sblundy の答えは素晴らしいですが、匿名の内部クラスには 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);

しかし、不変クラスの方が安全です。このようなクラスを可変にする正当な理由が思いつきません。

匿名の内部クラスを聞くたびにうんざりするので、私は本当にこれを投稿するだけです.プログラマーが最初に行ったのは、実際のクラスを使用する必要があり、決して彼の決定を再考しました。

于 2010-05-04T18:44:01.753 に答える
6

非常に人気が高まっているGoogle Guava ライブラリには、API の多くの部分に組み込まれた汎用のFunctionおよびPredicateオブジェクトがあります。

于 2010-12-16T21:32:14.177 に答える
5

Java でプログラミングするときに本当に恋しいものの 1 つは、関数のコールバックです。これらの必要性が常に存在する状況の 1 つは、各アイテムに対して特定のアクションを実行する階層を再帰的に処理する場合でした。ディレクトリ ツリーをたどったり、データ構造を処理したりするようなものです。私の内部のミニマリストは、特定のケースごとにインターフェイスを定義してから実装する必要があることを嫌います。

ある日、なぜだろうと思っていました。メソッド ポインター、つまり Method オブジェクトがあります。JIT コンパイラーを最適化することで、リフレクティブ呼び出しによってパフォーマンスが大幅に低下することはなくなりました。そして、たとえば、ある場所から別の場所にファイルをコピーすることに加えて、反映されたメソッド呼び出しのコストは取るに足らないものになります。

さらに考えてみると、OOP パラダイムのコールバックでは、オブジェクトとメソッドを一緒にバインドする必要があることに気付きました。Callback オブジェクトに入ります。

Callbacks in Javaのリフレクション ベースのソリューションを確認してください。無料でご利用いただけます。

于 2010-01-28T03:20:55.163 に答える
4

関数の配列のインターフェイスを使用せずに同じことを行うには、次のようにします。

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); } }
于 2011-03-18T02:53:57.243 に答える
4

私には戦略パターンのように聞こえます。fancycat.com の Java パターンを調べてください。

于 2008-09-25T03:21:47.003 に答える
4

わかりました、このスレッドはすでに十分に古いので、おそらく私の答えは質問には役に立ちません. しかし、このスレッドは私の解決策を見つけるのに役立ったので、とにかくここに掲載します。

既知の入力と既知の出力 (両方とも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 行のコードで、クラスもインターフェイスも必要ありません。これは悪くありません。

于 2011-11-09T14:59:09.500 に答える
3

うわー、私がすでに Java に対して行っていることを考えると、それほど難しくない Delegate クラスを作成し、それを使用して T が戻り値の型であるパラメーターを渡すだけではどうですか。申し訳ありませんが、Java を学んでいる C++/C# プログラマーとして、非常に便利な関数ポインターが必要です。メソッド情報を扱うクラスに精通している場合は、それを行うことができます。java.lang.reflect.method.

常にインターフェイスを使用する場合は、常にそれを実装する必要があります。イベントハンドリングでは、ハンドラーのリストへの登録/登録解除を回避するより良い方法は実際にはありませんが、値の型ではなく関数を渡す必要があるデリゲートの場合は、デリゲートクラスを作成して、アウトクラスのインターフェイスを処理します。

于 2011-03-02T04:11:04.413 に答える
3

ラムダジを調べる

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

特に新しい閉鎖機能

http://code.google.com/p/lambdaj/wiki/閉鎖

無意味なインターフェイスを作成したり、醜い内部クラスを使用したりせずに、クロージャーまたは関数ポインターを定義する非常に読みやすい方法を見つけることができます。

于 2009-09-12T07:39:12.667 に答える
1

動作を定義するために 1 つのパラメーター セットを使用するが、Scheme のように実行する別のパラメーター セットを使用する関数を渡すのに苦労している場合:

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

Java でのパラメータ定義の動作を伴う関数の受け渡しを参照してください。

于 2014-06-10T23:02:32.423 に答える