Java의 함수 포인터


148

이것은 일반적이고 사소한 것일 수 있지만 구체적인 답변을 찾는 데 어려움을 겪고있는 것 같습니다. C #에는 델리게이트의 개념이 있는데, 이는 C ++의 함수 포인터 개념과 밀접한 관련이 있습니다. Java에도 비슷한 기능이 있습니까? 포인터가 다소 없다는 것을 감안할 때 가장 좋은 방법은 무엇입니까? 그리고 분명히, 우리는 여기서 일등석으로 이야기하고 있습니다.


1
리스너 또는 다른 OOP 구문이 객체 특성을 유지하면서 동일한 작업을 수행하는 곳에서 왜 이것을 원할 지 궁금합니다. 나는 개념에 의해 제공되는 기능성에 대한 요구가 이해하기 쉽다는 것을 의미한다. 물론, 뭔가 빠진 것이 아니라면이 질문을한다! :-)
Newtopian

2
Java 8에는 람다식이 있습니다. docs.oracle.com/javase/tutorial/java/javaOO/… 체크 아웃하고 싶을 수 있습니다. 함수 포인터는 아니지만 여전히 더 많이 사용될 수 있습니다.
FrustratedWithFormsDesigner

6
Java 8 메소드 참조는 정확히 당신이 요구하는 것입니다.
Steven

8
Java 8 메소드 참조 는 정확히 당신이 요구하는 것입니다. this::myMethod의미 적으로 람다를 만드는 것과 동일 paramA, paramB -> this.myMethod(paramA, paramB)합니다.
Steven

답변:


126

함수 포인터와 같은 기능에 대한 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);

2
전략 디자인 패턴. 함수가 함수에 대한 인수로 제공되지 않은 일부 전역이 아닌 데이터를 함수에서 사용하려는 경우 C 또는 C ++ 함수 포인터만큼보기 흉하지 않습니다.
Raedwald

75
@Raedwald C ++에는 functors가 있고 C ++ 0x에는 functors 위에 클로저와 람다가 내장되어 있습니다. std::bind매개 변수를 함수에 바인딩하고 호출 가능한 객체를 반환하는 조차 있습니다 . 이러한 이유로 C를 방어 할 수는 없지만 C ++ Java보다 실제로 좋습니다.
zneak

21
@zneak, @Raedwald : C에서 사용자 데이터에 대한 포인터를 전달하면됩니다 (예 : struct). (그리고 새로운 수준의 콜백 간접 참조로 캡슐화 할 수 있습니다) 실제로 상당히 깨끗합니다.
brice

25
예, 실제로 매일 crufty 래퍼 클래스에 대해 C 함수 포인터를 사용합니다
BT

3
Java 8에는 더 좋은 구문이 있습니다.
Thorbjørn Ravn Andersen

65

인터페이스로 함수 포인터를 대체 할 수 있습니다. 컬렉션을 살펴보고 각 요소로 무언가를 수행한다고 가정 해 봅시다.

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

42

반사를 사용하여 수행 할 수 있습니다.

객체와 메소드 이름을 문자열로 매개 변수로 전달한 다음 메소드를 호출하십시오. 예를 들면 다음과 같습니다.

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

물론 모든 예외를 확인하고 필요한 캐스트를 추가하십시오.


7
리플렉션은 "느리게 느린 자바 코드를 작성하는 방법"을 제외하고는 정답이 아닙니다. : D
Jacob

5
그것은 "추악한"느릴 수도 있지만, 리플렉션을 통해 허용 된 답변의 인터페이스 기반 솔루션이 할 수없는 일을 할 수 있다고 생각합니다 (실수하지 않은 한), 즉 동일한 코드 내에서 동일한 객체의 다양한 메소드를 호출합니다. 일부.
유세비우스

@zoquete .. 나는이 게시물을 겪고 있었고 의심이 .. 그래서 당신의 접근 방식에서 변수 "theDescription"에 액세스하면 변수에 액세스 할 때마다 함수가 호출됩니다.
karthik27

20

아니요, 함수는 Java의 첫 번째 클래스 객체가 아닙니다. 핸들러 클래스를 구현하여 동일한 작업을 수행 할 수 있습니다. 이것은 Swing 등에서 콜백이 구현되는 방식입니다.

그러나 자바의 차기 버전에서는 클로저 (당신이 말하는 것에 대한 공식 이름)에 대한 제안이 있습니다 -Javaworld 는 흥미로운 기사를 가지고 있습니다.


15

이것은 Steve Yegge의 명사에서처형 을 생각 나게한다 . 기본적으로 Java에는 모든 작업에 객체가 필요하므로 함수 포인터와 같은 "동사 전용"엔터티가 없습니다.


8

비슷한 기능을 달성하기 위해 익명의 내부 클래스를 사용할 수 있습니다.

인터페이스를 정의하려는 경우 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.
    }
}

7

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

의 개념이 aliaspublic List<T> collect(T t) = Collections::collect
있습니까

내가 아는 한 @javadba.
Alex

5

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

3

리플렉션을 사용하여 Java에서 콜백 / 델리게이트 지원을 구현했습니다. 자세한 내용과 작업 소스는 내 웹 사이트에서 확인할 수 있습니다 .

작동 원리

WithParms라는 중첩 클래스가있는 Callback이라는 기본 클래스가 있습니다. 콜백이 필요한 API는 콜백 객체를 매개 변수로 사용하고 필요한 경우 메서드 변수로 Callback.WithParms를 만듭니다. 이 객체의 많은 응용 프로그램이 재귀 적이므로 매우 깨끗하게 작동합니다.

성능이 여전히 나에게 우선 순위가 높기 때문에 모든 호출에 대한 매개 변수를 보유하기위한 객관적인 객체 배열을 만들고 싶지 않았습니다. 결국 큰 데이터 구조에는 수천 개의 요소가 있으며 메시지 처리가 가능합니다. 시나리오 우리는 초당 수천 개의 데이터 구조를 처리 할 수 ​​있습니다.

스레드 안전을 위해서는 매개 변수 배열이 API 메소드를 호출 할 때마다 고유하게 존재해야하며 효율성을 위해 콜백을 호출 할 때마다 동일한 배열을 사용해야합니다. 콜백을 호출하기 위해 매개 변수 배열로 바인딩하기 위해 만들 저렴한 두 번째 객체가 필요했습니다. 그러나 일부 시나리오에서는 호출자가 다른 이유로 이미 매개 변수 배열을 가지고있을 것입니다. 이 두 가지 이유로 인해 매개 변수 배열은 콜백 객체에 속하지 않았습니다. 또한 매개 변수를 배열 또는 개별 객체로 전달하는 호출 선택은 콜백을 사용하여 API의 손에 속하며 내부 작업에 가장 적합한 호출을 사용할 수 있습니다.

WithParms 중첩 클래스는 선택 사항이며 두 가지 용도로 사용되며 콜백 호출에 필요한 매개 변수 객체 배열을 포함하며 매개 변수 배열을로드 한 다음 10 개의 오버로드 된 invoke () 메서드 (1-10 개의 매개 변수 포함)를 제공합니다. 콜백 대상을 호출하십시오.



-1

여기에있는 대부분의 사람들과 관련하여 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();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.