web-dev-qa-db-ja.com

パラメータとしてのJava Passメソッド

メソッドを参照渡しする方法を探しています。私は、Javaがメソッドをパラメータとして渡さないことを理解しています、しかし私は代わりを得たいと思います。

インターフェイスはメソッドとしてパラメータとして渡すことの代替手段であると言われてきましたが、参照によってインターフェイスがメソッドとしてどのように機能するかはわかりません。私が正しく理解していれば、インターフェースは定義されていない抽象的なメソッドのセットです。毎回定義する必要があるインターフェースを送信したくありません。複数の異なるメソッドが同じパラメーターを使用して同じメソッドを呼び出す可能性があるためです。

私が達成したいのは、これに似たものです。

public void setAllComponents(Component[] myComponentArray, Method myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod(leaf);
    } //end looping through components
}

次のように呼び出されます。

setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());
237
Mac

Edit:Java 8以降、 ラムダ式otherとして素晴らしい解決策です。回答 が指摘している。以下の答えはJava 7以前のために書かれました...


コマンドパターン を見てください。

// NOTE: code not tested, but I believe this is valid Java...
public class CommandExample 
{
    public interface Command 
    {
        public void execute(Object data);
    }

    public class PrintCommand implements Command 
    {
        public void execute(Object data) 
        {
            System.out.println(data.toString());
        }    
    }

    public static void callCommand(Command command, Object data) 
    {
        command.execute(data);
    }

    public static void main(String... args) 
    {
        callCommand(new PrintCommand(), "hello world");
    }
}

編集:Pete Kirkhamが で指摘しているように、 訪問者 。ビジターのアプローチはもう少し複雑です - あなたのノードはすべてacceptVisitor()メソッドでビジターを意識する必要があります - しかしあなたがもっと複​​雑なオブジェクトグラフをトラバースする必要があるならそれは調べる価値があります。

210
Dan Vinton

Java 8では、 Lambda Expressions およびMethod Referencesを使用して、メソッドをより簡単に渡すことができます。まず、いくつかの背景:機能的インターフェースは、抽象メソッドを1つだけ持つインターフェースです。ただし、 デフォルトメソッド (Java 8の新機能)と静的メソッドをいくつでも含めることができます。ラムダ式を使用しない場合は、不要な構文を使用することなく、ラムダ式を使用して抽象メソッドを迅速に実装できます。

ラムダ式がない場合

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

ラムダ式では:

obj.aMethod(i -> i == 982);

これは Lambda式に関するJavaチュートリアル からの抜粋です。

ラムダ式の構文

ラムダ式は次のもので構成されています。

  • かっこで囲まれた仮パラメータのカンマ区切りリスト。 CheckPerson.testメソッドには、Personクラスのインスタンスを表す1つのパラメーターpが含まれています。

    :ラムダ式のパラメータのデータ型は省略できます。さらに、パラメーターが1つしかない場合は、括弧を省略することができます。たとえば、次のラムダ式も有効です。

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    
  • 矢印トークン->

  • 本体。単一の式またはステートメントブロックで構成されています。この例では次の式を使用します。

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    

    単一の式を指定した場合、Javaランタイムはその式を評価してからその値を返します。あるいは、returnステートメントを使うことができます。

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
    

    Return文は式ではありません。ラムダ式では、ステートメントを中括弧({})で囲む必要があります。ただし、voidメソッド呼び出しを中括弧で囲む必要はありません。たとえば、次は有効なラムダ式です。

    email -> System.out.println(email)
    

ラムダ式はメソッド宣言とよく似ています。ラムダ式は無名メソッド、つまり名前のないメソッドと見なすことができます。


これが、ラムダ式を使って「メソッドを渡す」方法です。

interface I {
    public void myMethod(Component component);
}

class A {
    public void changeColor(Component component) {
        // code here
    }

    public void changeSize(Component component) {
        // code here
    }
}
class B {
    public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
        for(Component leaf : myComponentArray) {
            if(leaf instanceof Container) { // recursive call if Container
                Container node = (Container)leaf;
                setAllComponents(node.getComponents(), myMethodInterface);
            } // end if node
            myMethodsInterface.myMethod(leaf);
        } // end looping through components
    }
}
class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
        b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
    }
}

以下のようなメソッド参照を使用することで、クラスCをさらに短くすることができます。

class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), a::changeColor);
        b.setAllComponents(this.getComponents(), a::changeSize);
    }
}
59

Java.lang.reflect.Methodオブジェクトを使用してinvokeを呼び出す

最初にパラメータとして渡したいメソッドを使ってインタフェースを定義します。

public interface Callable {
  public void call(int param);
}

メソッドでクラスを実装する

class Test implements Callable {
  public void call(int param) {
    System.out.println( param );
  }
}

//そのように呼び出す

Callable cmd = new Test();

これにより、cmdをパラメータとして渡して、インタフェースで定義されているメソッド呼び出しを呼び出すことができます。

public invoke( Callable callable ) {
  callable.call( 5 );
}
17
stacker

これはJava 7以下ではまだ有効ではありませんが、将来を見据えて、少なくともJava 8などの新しいバージョンで登場する 変更 を認識する必要があると思います。

つまり、この新しいバージョンでは lambdas と、(この問題に対する別の有効な解決策である new APIs と共に)Javaへのメソッド参照を提供しています。 JVMによる処理が異なるため、追加のクラスファイルは出力ディレクトリを汚染する必要がありません。

両方のフレーバー(ラムダとメソッド参照)は、シグネチャが使用されている単一のメソッドで利用可能なインターフェースを必要とします。

public interface NewVersionTest{
    String returnAString(Object oIn, String str);
}

メソッドの名前はこれからは関係ありません。ラムダが受け入れられるところでは、メソッド参照も同様です。たとえば、私たちの署名をここで使うには、

public static void printOutput(NewVersionTest t, Object o, String s){
    System.out.println(t.returnAString(o, s));
}

これは、ラムダまでの、単純なインターフェース呼び出しです。1 渡される:

public static void main(String[] args){
    printOutput( (Object oIn, String sIn) -> {
        System.out.println("Lambda reached!");
        return "lambda return";
    }
    );
}

これは出力されます:

Lambda reached!
lambda return

メソッド参照は似ています。与えられた:

public class HelperClass{
    public static String testOtherSig(Object o, String s){
        return "real static method";
    }
}

そしてメイン:

public static void main(String[] args){
    printOutput(HelperClass::testOtherSig);
}

出力はreal static methodになります。 メソッド参照は静的、インスタンス、任意のインスタンスを持つ非静的、そしてコンストラクタさえも可能です 。コンストラクタにはClassName::newに似たものが使用されます。

1 副作用があるので、これはラムダとは見なされません。ただし、これを使用してよりわかりやすく視覚化する方法は説明されています。

12
Andrey Akhmetov

前回チェックしたときには、Javaはあなたが望むことをネイティブに行うことはできません。そのような制限を回避するには、「回避策」を使用する必要があります。私が見る限りでは、インターフェースは代替案ですが、良い代替案ではありません。おそらく、それを意味していると言った人がいます。

public interface ComponentMethod {
  public abstract void PerfromMethod(Container c);
}

public class ChangeColor implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public class ChangeSize implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod.PerfromMethod(leaf);
    } //end looping through components
}

あなたはそれから次のものを呼び出します:

setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());
11
user246091

Java 8以降、メソッドを持つFunction<T, R>インターフェース( docs )があります。

R apply(T t);

他の関数にパラメータとして関数を渡すためにそれを使うことができます。 Tは関数の入力型、Rは戻り型です。

あなたの例では、入力としてComponent型を取り、何も返さない関数 - Voidを渡す必要があります。この場合、Void型の自動ボクシングがないため、Function<T, R>は最良の選択ではありません。あなたが探しているインターフェースはmethodでConsumer<T>docs )と呼ばれます

void accept(T t);

これは次のようになります。

public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { 
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } 
        myMethod.accept(leaf);
    } 
}

そして、あなたはメソッド参照を使ってそれを呼ぶでしょう:

setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize); 

ChangeColor()メソッドとchangeSize()メソッドを同じクラスに定義したとします。


もしあなたのメソッドがたまたま複数のパラメータを受け付けるのであれば、BiFunction<T, U, R> - TとUを入力パラメータの型として、そしてRをリターン型として使うことができます。 BiConsumer<T, U>(2つの引数、戻り値の型なし)もあります。残念ながら、3つ以上の入力パラメータの場合は、自分でインタフェースを作成する必要があります。例えば:

public interface Function4<A, B, C, D, R> {

    R apply(A a, B b, C c, D d);
}
8
JakubM

これらのメソッドで何かを返す必要がない場合は、Runnableオブジェクトを返させることができます。

private Runnable methodName (final int arg){
    return new Runnable(){
       public void run(){
          // do stuff with arg
       }
    }
}

それを次のように使います。

private void otherMethodName (Runnable arg){
    arg.run();
}
6
Smig

Observerパターンを使用します(Listenerパターンとも呼ばれます)。

interface ComponentDelegate {
    void doSomething(Component component);
}

public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
    // ...
    delegate.doSomething(leaf);
}

setAllComponents(this.getComponents(), new ComponentDelegate() {
                                            void doSomething(Component component) {
                                                changeColor(component); // or do directly what you want
                                            }
                                       });

new ComponentDelegate()...はインターフェースを実装する無名型を宣言します。

1
EricSchaefer

パラメータ関数として単純なメソッドにJava.util.function.Functionを使用する方法について、私には十分明示的な例は見当たりませんでした。これは簡単な例です:

import Java.util.function.Function;

public class Foo {

  private Foo(String parameter) {
    System.out.println("I'm a Foo " + parameter);
  }

  public static Foo method(final String parameter) {
    return new Foo(parameter);
  }

  private static Function parametrisedMethod(Function<String, Foo> function) {
    return function;
  }

  public static void main(String[] args) {
    parametrisedMethod(Foo::method).apply("from a method");
  }
}

基本的にはデフォルトコンストラクタを持つFooオブジェクトがあります。タイプがFunction<String, Foo>であるmethodからパラメーターとして呼び出されるparametrisedMethod

  • Function<String, Foo>は、関数がパラメータとしてStringを受け取り、Fooを返すことを意味します。
  • Foo::Methodx -> Foo.method(x);のようなラムダに対応します
  • parametrisedMethod(Foo::method)x -> parametrisedMethod(Foo.method(x))と見なすことができます
  • .apply("from a method")は基本的にparametrisedMethod(Foo.method("from a method"))を実行することです

これは出力に返されます。

>> I'm a Foo from a method

この例はそのまま実行する必要があります。その場合は、さまざまなクラスやインタフェースを使用して、上記の回答からより複雑なものを試すことができます。

1
Sylhare

Javaには名前を渡して呼び出すメカニズムがあります。それは反射メカニズムの一部です。あなたの関数はMethodクラスの追加パラメータを取るべきです。

public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}
1
David Gruzman

私はJavaのエキスパートではありませんが、あなたの問題を次のように解決します。

@FunctionalInterface
public interface AutoCompleteCallable<T> {
  String call(T model) throws Exception;
}

私は私の特別なインターフェースでパラメータを定義します

public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {.......
//call here
String value = getSearchText.call(item);
...
}

最後に、initialize methodを呼び出しながらgetSearchText methodを実装します。

initialize(getMessageContactModelList(), new AutoCompleteCallable() {
          @Override
          public String call(Object model) throws Exception {
            return "custom string" + ((xxxModel)model.getTitle());
          }
        })
0
Sercan özen

ラムダはこの目的のためのものではないと思います... Javaは関数型プログラミング言語ではないため、決してそうなることはありません。メソッドとしてパラメータとして渡すことはありません。そうは言っても、Javaはオブジェクト指向であり、それを念頭に置いて私たちは望むことなら何でもできるということを忘れないでください。最初のアイデアは、単に「メソッドを含むオブジェクト」をパラメータとして渡すことです。そのため、メソッドを「渡す」必要があるときはいつでも、単にそのクラスのインスタンスを渡すだけです。メソッドを定義するときは、そのメソッドを含むクラスのインスタンスをパラメータとして追加する必要があります。これは機能するはずですが、クラスコードにアクセスできない場合はメソッドを再定義できず、多くの場合は不可能なので、これは望ましくありません。さらに、誰かがパラメータとしてメソッドを渡す必要がある場合、それはメソッドの動作が動的であるべきだからだと思います。つまり、あなたのクラスを使うプログラマは、そのメソッドが返すべきものを選ぶことはできるが、その型は選ぶことができないということです。幸いなことに、Javaには美しく単純な解決策、つまり抽象クラスがあります。簡単に言うと、抽象クラスは、メソッドの「シグネチャ」を知っていてもその動作がわからないときに使用されます。メソッドの名前と型を抽象クラスでラップし、そのクラスのインスタンスをメソッドのパラメータ...待つ...これは以前と同じではないですか?そして抽象クラスのインスタンスを持つことができますか?いいえおよびいいえ...しかし抽象メソッドを作成するときにははい...また、抽象クラスを拡張するクラスでそれを再定義する必要があります。Javaの動的バインディングのため、Javaは(静的、非公開などを宣言しない限り)常に再定義されたバージョンを使用します。関数を数値の配列に適用したいとします。したがって、入出力を2乗したい場合は[1,2,3,4、...] - > [1,4,9,16]のようにします。 、... ...(haskellのような関数型プログラミング言語では、これは 'map'のようないくつかのツールのおかげで簡単なことです、...)二乗数について特別なことは何もないことに注意してください。 t。そのため、コードは[args]、f - > [f(args)]のようになります。 Javaに戻る=>関数は単なるメソッドなので、欲しいのは配列に別の関数を適用する関数です。一言で言えば、パラメータとしてメソッドを渡す必要があります。これが私のやり方です==>

1)ラッパー抽象クラスと方法の定義

public  abstract class Function 
{
    public abstract double f(double x);
}

2)APPLY_TO_ARRAYメソッドでクラスを定義する

public class ArrayMap 
{
public static double[] apply_to_array(double[] arr, Function fun)
{
    for(int i=0; i<arr.length;i++)
    {
        arr[i]=fun.f(arr[i]);
    }
    return arr;
}
}

3)テスタークラスを作成し、いくつかの楽しみを持ってください

public class Testclass extends Function
{
    public static void main(String[] args) 
    {
        double[] myarr = {1,2,3,4};
        ArrayMap.apply_to_array(myarr, new Testclass());
        for (double k : myarr)
        {
        System.out.println(k);
        }

    }

    @Override
    public double f(double x) 
    {

        return Math.log(x);
    }   
}

Function型のオブジェクトを渡す必要があり、Testclassはそれを使用できるFunctionクラスを拡張するため、キャストは自動的に行われます。

0
omar kahol

これが基本的な例です。

public class TestMethodPassing
{
    private static void println()
    {
        System.out.println("Do println");
    }

    private static void print()
    {
        System.out.print("Do print");
    }

    private static void performTask(BasicFunctionalInterface functionalInterface)
    {
        functionalInterface.performTask();
    }

    @FunctionalInterface
    interface BasicFunctionalInterface
    {
        void performTask();
    }

    public static void main(String[] arguments)
    {
        performTask(TestMethodPassing::println);
        performTask(TestMethodPassing::print);
    }
}

出力:

Do println
Do print
0
BullyWiiPlaza

メソッドのパラメータとしてバインドされたパラメータを使用してmethodを渡す方法を示す解決策は、ここでは見つかりませんでした。ベローは、パラメータ値がすでにバインドされているメソッドを渡す方法の例です。

  1. 手順1:戻り型を持つインターフェイスと、返さないインターフェイスを持つインターフェイスを2つ作成します。 Javaにも似たようなインタフェースがありますが、例外スローをサポートしていないため、実用的ではありません。


    public interface Do {
    void run() throws Exception;
    }


    public interface Return {
        R run() throws Exception;
    }

  1. トランザクションでメソッド呼び出しをラップするために両方のインターフェースを使用する方法の例。 methodを実際のパラメータで渡すことに注意してください。


    //example - when passed method does not return any value
    public void tx(final Do func) throws Exception {
        connectionScope.beginTransaction();
        try {
            func.run();
            connectionScope.commit();
        } catch (Exception e) {
            connectionScope.rollback();
            throw e;
        } finally {
            connectionScope.close();
        }
    }

    //Invoke code above by 
    tx(() -> api.delete(6));

別の例は、実際に何かを返すメソッドを渡す方法を示しています



        public  R tx(final Return func) throws Exception {
    R r=null;
    connectionScope.beginTransaction();
    try {
                r=func.run();
                connectionScope.commit();
            } catch (Exception e) {
                connectionScope.rollback();
                throw e;
            } finally {
                connectionScope.close();
            }
        return r;
        }
        //Invoke code above by 
        Object x= tx(() -> api.get(id));

0
Stan Sokolov