source

자바 대리인?

goodcode 2022. 7. 28. 23:51
반응형

자바 대리인?

Java 언어에는 C#이 대리인을 지원하는 것과 유사한 위임 기능이 있습니까?

그렇지 않아, 아니야.

리플렉션을 사용하여 메서드 객체를 호출할 수 있는 경우에도 같은 효과를 얻을 수 있습니다.또 다른 방법은 단일 '호출' 메서드 또는 '실행' 메서드로 인터페이스를 만든 후 관심 있는 메서드를 인스턴스화(즉, 익명 내부 클래스 사용)하는 것입니다.

이 기사도 흥미롭거나 도움이 될 수 있습니다.자바 프로그래머가 C# 대리인을 조사합니다(@blueskyprojects.com)

정확한 의미에 따라 전략 패턴을 사용하여 유사한 효과(메서드 전달)를 얻을 수 있습니다.

다음과 같은 행이 명명된 메서드시그니처를 선언하는 대신:

// C#
public delegate void SomeFunction();

인터페이스를 선언합니다.

// Java
public interface ISomeBehaviour {
   void SomeFunction();
}

메서드의 구체적인 구현을 위해 동작을 구현하는 클래스를 정의합니다.

// Java
public class TypeABehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeA behaviour
   }
}

public class TypeBBehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeB behaviour
   }
}

서든 있을 수 일이라면SomeFunction에서 위임, C#을 합니다.ISomeBehaviour신신: :

// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

익명 내부 클래스를 사용하면 개별 명명된 클래스를 선언하지 않고 실제 위임 함수처럼 처리할 수도 있습니다.

// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

SomeMethod(new ISomeBehaviour() { 
   @Override
   public void SomeFunction() {
      // your implementation
   }
});

이 기능은 구현이 현재 컨텍스트에 매우 고유하고 재사용해도 도움이 되지 않는 경우에만 사용해야 합니다.

물론 Java 8에서는 기본적으로 람다 표현이 됩니다.

// Java 8
SomeMethod(() -> { /* your implementation */ });

Short story: ­­­­­­­­­­­­­­­­­­­no.

서론

Microsoft Visual J++ 개발 환경의 최신 버전은 위임 또는 바인딩된 메서드 참조라는 언어 구조를 지원합니다.이 구성 및 새로운 키워드delegate ★★★★★★★★★★★★★★★★★」multicast이를 지원하기 위해 도입된는 Java 프로그래밍TM 언어의 일부가 아닙니다.JDKTM 1.1 소프트웨어 매뉴얼포함된 내부 클래스 사양에 의해 수정되고 Java 언어 사양에 의해 지정됩니다.

Java 프로그래밍 언어가 이 구성을 포함할 가능성은 거의 없습니다.Sun은 이미 1996년에 작업용 프로토타입을 제작하고 폐기하는 정도까지 채택을 신중히 검토했습니다.우리의 결론은 바인딩된 방법 참조가 불필요하고 언어에 해롭다는 것이었다.이 결정은 Delphi Object Pascal의 바운드 메서드 참조 경험이 있는 Borland International과 협의하여 이루어졌다.

다른 설계 대안인 내부 클래스는 동등하거나 우수한 기능을 제공하기 때문에 바운드 방법 참조는 불필요하다고 생각합니다.특히 내부 클래스는 사용자 인터페이스 이벤트 처리 요건을 완전히 지원하며 적어도 Windows Foundation 클래스만큼 포괄적인 사용자 인터페이스 API를 구현하기 위해 사용되고 있습니다.

바인딩된 메서드 참조는 Java 프로그래밍 언어의 단순성과 API의 광범위한 객체 지향 특성을 훼손하기 때문에 유해하다고 생각합니다.바인딩된 메서드 참조는 언어 구문 및 범위 지정 규칙에 불규칙성을 가져옵니다.마지막으로 VM은 추가 및 상이한 유형의 참조 및 메서드 연동을 효율적으로 처리해야 하므로 VM 기술에 대한 투자를 희석합니다.

읽어보셨나요?

대리인은 이벤트 기반 시스템에서 유용한 구성 요소입니다.기본적으로 위임은 지정된 개체에서 메서드 디스패치를 인코딩하는 개체입니다.이 문서에서는 Java 내부 클래스가 이러한 문제에 대한 보다 일반적인 해결책을 제공하는 방법을 보여 줍니다.

대리인이란?실제로 C++에서 사용되는 멤버 함수에 대한 포인터와 매우 유사합니다.그러나 대리자에는 호출할 메서드와 함께 대상 개체가 포함되어 있습니다.이상적으로는 다음과 같은 말을 할 수 있으면 좋겠습니다.

obj.registerHandler(ano.methodOne);

methodOne은 특정 이벤트가 수신되면 ano에서 호출됩니다.

이것이 위임 구조가 실현하는 것입니다.

Java 내부 클래스

Java는 익명의 내부 클래스를 통해 이 기능을 제공하므로 추가 위임 구성이 필요하지 않다는 주장이 제기되었습니다.

obj.registerHandler(new Handler() {
        public void handleIt(Event ev) {
            methodOne(ev);
        }
      } );

언뜻 보기에는 이것은 맞는 것처럼 보이지만 동시에 성가신 일이다.많은 이벤트 처리 예에서 위임 구문의 단순성이 매우 매력적이기 때문입니다.

일반 핸들러

그러나 이벤트 기반 프로그래밍이 일반적인 비동기 프로그래밍 환경의 일부로서 보다 광범위한 방식으로 사용되면 더 큰 위험이 따릅니다.

이러한 일반적인 상황에서는 대상 메서드와 대상 객체 인스턴스만 포함하는 것만으로는 충분하지 않습니다.일반적으로 이벤트 핸들러가 등록되었을 때 컨텍스트 내에서 결정되는 다른 파라미터가 필요할 수 있습니다.

이 일반적인 상황에서는 Java 접근 방식이 특히 최종 변수와 결합되어 매우 우아한 솔루션을 제공할 수 있습니다.

void processState(final T1 p1, final T2 dispatch) { 
  final int a1 = someCalculation();

  m_obj.registerHandler(new Handler() {
    public void handleIt(Event ev) {
     dispatch.methodOne(a1, ev, p1);
    }
  } );
}

최종 * 최종 * 최종

주목받았나?

최종 변수는 익명 클래스 메서드 정의 내에서 액세스할 수 있습니다.이 코드를 주의 깊게 검토하여 결과를 이해하십시오.이것은 잠재적으로 매우 강력한 기술입니다.예를 들어 MiniDOM에 핸들러를 등록하거나 보다 일반적인 상황에서 핸들러를 효과적으로 사용할 수 있습니다.

반면 위임 구성에서는 이 보다 일반적인 요건에 대한 해결책을 제공하지 않으므로 설계의 기반이 될 수 있는 관용어로 거부해야 합니다.

이 게시물이 오래된 것은 알지만 Java 8에서는 lamda와 기능 인터페이스라는 개념이 추가되어 있습니다.이것은 하나의 메서드로 이루어진 인터페이스입니다.이러한 기능은 모두 C# 딜러와 동일한 기능을 제공합니다.자세한 내용은 여기를 참조하거나 Java Lambdas를 검색하십시오.http://cr.openjdk.java.net/ ~ briangoetz / briangda / briangda - state - final . briangoetz

아니요, 하지만 프록시와 반사를 사용하여 가짜로 만들 수 있습니다.

  public static class TestClass {
      public String knockKnock() {
          return "who's there?";
      }
  }

  private final TestClass testInstance = new TestClass();

  @Test public void
  can_delegate_a_single_method_interface_to_an_instance() throws Exception {
      Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

이 관용구의 장점은 위임자를 작성하는 시점에서 위임 대상 메서드가 존재하며 필요한 시그니처가 있는지 확인할 수 있다는 것입니다(불행히도 FindBugs 플러그인은 여기서 도움이 될 수 있지만).그것을 사용하여 안전하게 다양한 인스턴스에 위임할 수 있습니다.

테스트구현에 대한 자세한 내용은 github의 karg 코드를 참조하십시오.

예, 아니오입니다만, Java의 위임 패턴은 다음과 같이 생각할 수 있습니다.이 비디오 튜토리얼은 액티비티 - fragment 간의 데이터 교환에 관한 것으로, 인터페이스를 이용한 위임 정렬 패턴의 훌륭한 본질을 가지고 있습니다.

자바 인터페이스

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

구조

Callback이라는 이름의 기본 클래스와 WithParms라는 이름의 중첩 클래스가 있습니다.콜백을 필요로 하는 API는 Callback 객체를 파라미터로 하여 필요에 따라 Callback을 작성합니다.메서드 변수로서의 WithParms.이 오브젝트의 어플리케이션의 대부분은 재귀적이기 때문에 매우 깔끔하게 동작합니다.

퍼포먼스가 여전히 높은 우선 순위이기 때문에 기동할 때마다 파라미터를 유지하는 일회용 객체 어레이를 만들고 싶지 않았습니다.결국 대규모 데이터 구조에서는 수천 개의 요소가 존재할 수 있으며 메시지 처리 시나리오에서는 초당 수천 개의 데이터 구조를 처리할 수 있습니다.

스레드 세이프를 실현하기 위해서는 API 메서드의 각 호출에 대해 파라미터 배열이 고유하게 존재해야 하며 효율성을 위해 콜백의 호출마다 동일한 파라미터를 사용해야 합니다.콜백을 호출용 파라미터 배열로 바인드하기 위해 작성하는 비용이 저렴한 두 번째 오브젝트가 필요했습니다.단, 경우에 따라서는 다른 이유로 호출자에 파라미터 배열이 이미 있을 수 있습니다.이러한 두 가지 이유로 파라미터 배열은 Callback 객체에 속하지 않습니다.또한 호출 선택(파라미터를 배열 또는 개별 개체로 전달)은 내부 작업에 가장 적합한 호출을 사용할 수 있도록 콜백을 사용하여 API의 손에 있습니다.

WithParms 네스트클래스는 옵션이며 2가지 목적에 대응합니다.콜백 호출에 필요한 파라미터 오브젝트 배열이 포함되어 있으며 파라미터 배열을 로드한 후 콜백타깃을 호출하는 10개의 오버로드 invoke() 메서드(1~10개의 파라미터 포함)를 제공합니다.

다음으로 콜백을 사용한 디렉토리 트리 내의 파일 처리 예를 나타냅니다.이것은 처리할 파일만 카운트하고 미리 정해진 최대 크기를 초과하는 파일이 없는지 확인하는 초기 검증 경로입니다.이 경우 API 호출을 사용하여 인라인으로 콜백을 작성합니다.단, 대상 메서드를 정적 값으로 반영하여 매번 반영하지 않도록 합니다.

static private final Method             COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);

...

IoUtil.processDirectory(root,new Callback(this,COUNT),selector);

...

private void callback_count(File dir, File fil) {
    if(fil!=null) {                                                                             // file is null for processing a directory
        fileTotal++;
        if(fil.length()>fileSizeLimit) {
            throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
            }
        }
    progress("Counting",dir,fileTotal);
    }

IoUtil.process Directory():

/**
 * Process a directory using callbacks.  To interrupt, the callback must throw an (unchecked) exception.
 * Subdirectories are processed only if the selector is null or selects the directories, and are done
 * after the files in any given directory.  When the callback is invoked for a directory, the file
 * argument is null;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);</pre>
 * <p>
 * @return          The number of files processed.
 */
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
    return _processDirectory(dir,new Callback.WithParms(cbk,2),sel);
    }

static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) {
    int                                 cnt=0;

    if(!dir.isDirectory()) {
        if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; }
        }
    else {
        cbk.invoke(dir,(Object[])null);

        File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
        if(lst!=null) {
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

이 예는 이 접근방식의 장점을 보여줍니다.어플리케이션 고유의 로직이 콜백에 추상화되어 디렉토리 트리를 재귀적으로 걷기 위한 번거로움이 완전히 재사용 가능한 정적 유틸리티 방식으로 적절히 처리됩니다.또, 새로운 용도 마다 인터페이스의 정의와 실장에 드는 코스트를 반복할 필요는 없습니다.물론 인터페이스에 대한 주장은 구현 대상(단순히 문서화되어 있는 것이 아니라 적용되고 있는 것)에 대해 훨씬 더 명확하다는 것입니다.그러나 실제로는 콜백 정의를 올바르게 하는 것이 문제가 되지 않았습니다.

인터페이스의 정의와 실장은 그다지 나쁘지 않습니다(애플릿을 배포하는 경우는 제외합니다만, 실제로 추가 클래스의 작성을 회피하는 경우는 제외됩니다).단, 1개의 클래스에 복수의 콜백이 있는 경우, 이것이 가장 좋은 점입니다.배포된 애플리케이션에서 오버헤드가 추가된 개별 내부 클래스로 각각 밀어 넣어야 할 뿐만 아니라 프로그래밍이 매우 번거로울 뿐만 아니라 보일러 플레이트 코드도 모두 "소음"에 불과합니다.

있지 .delegate키워드를 C#으로 지정합니다만, Java 8 에서는, 기능 인터페이스(즉, 1 개의 메서드를 가지는 인터페이스)와 lamda 를 사용해 같은 것을 실현할 수 있습니다.

private interface SingleFunc {
    void printMe();
}

public static void main(String[] args) {
    SingleFunc sf = () -> {
        System.out.println("Hello, I am a simple single func.");
    };
    SingleFunc sfComplex = () -> {
        System.out.println("Hello, I am a COMPLEX single func.");
    };
    delegate(sf);
    delegate(sfComplex);
}

private static void delegate(SingleFunc f) {
    f.printMe();
}

의 모든 오브젝트SingleFunc 실시해야 한다printMe()다른 를 들어, 다른 방법)으로 넘겨주는 것이 안전합니다.delegate(SingleFunc)printMe()★★★★★★ 。

클래스 패스에 세이프티 미러를 사용하면 C#의 대표자 및 이벤트와 유사한 정보를 얻을 수 있습니다.

프로젝트의 README 예:

대표자(Java)!

  Delegate.With1Param<String, String> greetingsDelegate = new Delegate.With1Param<>();
  greetingsDelegate.add(str -> "Hello " + str);
  greetingsDelegate.add(str -> "Goodbye " + str);

  DelegateInvocationResult<String> invocationResult = 
  greetingsDelegate.invokeAndAggregateExceptions("Sir");

  invocationResult.getFunctionInvocationResults().forEach(funInvRes -> 

  System.out.println(funInvRes.getResult()));
  //prints: "Hello sir" and "Goodbye Sir"

이벤트

  //Create a private Delegate. Make sure it is private so only *you* can invoke it.
  private static Delegate.With0Params<String> trimDelegate = new Delegate.With0Params<>();

  //Create a public Event using the delegate you just created.
  public static Event.With0Params<String> trimEvent= new Event.With0Params<>(trimDelegate)

SO 답변도 참조하십시오.

C# 대리인은 Java Proxy를 사용하여 구현할 수 있습니다.

아뇨, 하지만 내부적으로는 비슷한 행동을 해요

C#에서는 딜러가 개별 진입점을 작성하는데 사용되며 이들은 함수 포인터와 거의 동일하게 동작합니다.

자바에서는 함수 포인터(어퍼 룩)는 없지만 내부적으로 이러한 목적을 달성하기 위해서는 동일한 작업을 수행해야 합니다.

예를 들어 Java에서 스레드를 작성하려면 클래스 객체 변수를 메모리 위치 포인터를 사용할 수 있으므로 스레드를 확장하거나 Runnable을 구현해야 합니다.

아니요, Java에는 그런 놀라운 기능이 없습니다.그러나 옵저버 패턴을 사용하여 수동으로 생성할 수 있습니다.다음은 예를 제시하겠습니다.C# 위임자를 Java로 씁니다.

이 코드는 C# 대리인의 많은 장점을 제공합니다.스태틱 방식 또는 다이내믹 방식 중 어느 쪽이든 동일한 방식으로 처리할 수 있습니다.사용자 코드에 추가 클래스가 필요 없다는 의미에서 리플렉션에 의한 호출 방식의 복잡성이 경감되고 코드가 재사용 가능합니다.오브젝트 배열을 작성하지 않고 파라미터가 1개인 메서드를 호출할 수 있는 invoke의 대체 편의 버전을 호출합니다.아래 자바 코드:

  class Class1 {
        public void show(String s) { System.out.println(s); }
    }

    class Class2 {
        public void display(String s) { System.out.println(s); }
    }

    // allows static method as well
    class Class3 {
        public static void staticDisplay(String s) { System.out.println(s); }
    }

    public class TestDelegate  {
        public static final Class[] OUTPUT_ARGS = { String.class };
        public final Delegator DO_SHOW = new Delegator(OUTPUT_ARGS,Void.TYPE);

        public void main(String[] args)  {
            Delegate[] items = new Delegate[3];

            items[0] = DO_SHOW .build(new Class1(),"show,);
            items[1] = DO_SHOW.build (new Class2(),"display");
            items[2] = DO_SHOW.build(Class3.class, "staticDisplay");

            for(int i = 0; i < items.length; i++) {
                items[i].invoke("Hello World");
            }
        }
    }

Java에는 대표자가 없으며 이를 자랑스럽게 생각합니다. :)여기서 읽은 바로는 딜러를 속이는 두 가지 방법을 본질적으로 발견했습니다. 1. 성찰; 2. 내부 클래스

반사가 스르륵 스르륵 스르륵!내부 클래스에서는 가장 단순한 사용 예: 정렬 함수는 다루지 않습니다.자세한 내용은 설명하지 않지만 기본적으로 inner 클래스를 사용하는 솔루션은 오름차순으로 정렬되는 정수 배열에 대한 래퍼 클래스와 내림차순으로 정렬되는 정수 배열에 대한 클래스를 만드는 것입니다.

언급URL : https://stackoverflow.com/questions/44912/java-delegates

반응형