[java] 파라미터로서의 Java 패스 메소드

참조로 메서드를 전달하는 방법을 찾고 있습니다. 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());



답변

편집 : Java 8부터 다른 답변 이 지적한 것처럼 람다 식은 훌륭한 솔루션 입니다. 아래 답변은 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");
    }
}

편집 :피트 Kirkham가 지적 하는 사용하여이 일을 다른 방법이 방문자가 . 방문자 접근 방식은 조금 더 복잡합니다. 노드는 모두 acceptVisitor()방법을 사용하여 방문자를 인식 해야하지만 더 복잡한 객체 그래프를 통과 해야하는 경우 검사 할 가치가 있습니다.


답변

Java 8에서는 이제 Lambda Expressions 및 Method References를 사용하여보다 쉽게 ​​메서드를 전달할 수 있습니다 . 첫째, 일부 배경 : 기능적 인터페이스는 하나의 추상 메소드를 가진 인터페이스이지만 여러 기본 메소드를 포함 할 수 있습니다 (Java 8의 새로운 기능) 및 정적 메소드를 . 람다 식을 사용하지 않으면 필요한 모든 구문없이 람다 식을 사용하여 추상 메서드를 빠르게 구현할 수 있습니다.

람다식이 없으면 :

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

람다 식의 경우 :

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

다음은 Lambda Expressions에 대한 Java 자습서에서 발췌 한 내용입니다 .

람다 식의 구문

람다 식은 다음과 같이 구성됩니다.

  • 괄호로 묶인 쉼표로 구분 된 공식 매개 변수 목록입니다. CheckPerson.test 메소드는 Person 클래스의 인스턴스를 나타내는 하나의 매개 변수 p를 포함합니다.

    참고 : 람다 식에서 매개 변수의 데이터 형식을 생략 할 수 있습니다. 또한 매개 변수가 하나만 있으면 괄호를 생략 할 수 있습니다. 예를 들어 다음 람다 식도 유효합니다.

    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;
    }

    리턴 문은 표현식이 아닙니다. 람다 식에서는 중괄호 ({})로 문을 묶어야합니다. 그러나 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);
    }
}


답변

java.lang.reflect.Method개체를 사용하여 전화invoke


답변

Java 8부터 메소드 가있는 Function<T, R>인터페이스 ( docs )가 있습니다.

R apply(T t);

이를 사용하여 함수를 다른 함수에 매개 변수로 전달할 수 있습니다. T는 함수의 입력 유형이고 R은 반환 유형입니다.

귀하의 예에서 Componenttype을 입력으로 사용하고 아무것도 반환하지 않는 함수를 전달해야합니다 Void. 이 경우 Function<T, R>Void 유형의 자동 박스가 없으므로 최선의 선택이 아닙니다. 찾고있는 인터페이스를 메소드와 함께 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>두 개의 인수, 반환 유형 없음이 있습니다. 불행히도 3 개 이상의 입력 매개 변수의 경우, 직접 인터페이스를 작성해야합니다. 예를 들면 다음과 같습니다.

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

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


답변

먼저 매개 변수로 전달할 메소드로 인터페이스를 정의하십시오.

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 );
}


답변

Java 7 이하에서는 아직 유효하지 않지만 미래를 바라보고 최소한 변경 사항을 인식해야한다고 생각합니다 Java 8과 같은 새로운 버전에서 을 .

즉,이 새 버전은 새로운 API 와 함께 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 부작용이 있기 때문에 일부에서는 람다로 간주되지 않습니다. 그러나보다 직관적 인 방식으로 사용하는 방법을 보여줍니다.


답변

마지막으로 확인했을 때 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());