Delegaci Javy?

Czy język Java ma funkcje delegatów, podobne do tego, jak C# obsługuje delegatów?

Author: Mark, 2008-09-05

11 answers

Niezupełnie.

Możesz osiągnąć ten sam efekt używając reflection, aby uzyskać Obiekty metody, które możesz następnie wywołać, a innym sposobem jest stworzenie interfejsu z pojedynczą metodą 'invoke' lub 'execute', a następnie utworzenie ich instancji, aby wywołać metodę, którą jesteś zainteresowany (tj. używając anonimowej klasy wewnętrznej).

Ten artykuł może być również interesujący / przydatny: programista Java patrzy na C#

 138
Author: Matt Sheppard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 22:54:49

W zależności od tego, co dokładnie masz na myśli, możesz osiągnąć podobny efekt (pomijając metodę) za pomocą wzorca strategii.

Zamiast takiej linii deklarującej nazwaną sygnaturę metody:

// C#
public delegate void SomeFunction();

Zadeklaruj interfejs:

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

Dla konkretnych implementacji metody zdefiniuj klasę, która implementuje zachowanie:

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

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

W takim razie gdziekolwiek miałbyś SomeFunction delegata w C#, użyj zamiast tego ISomeBehaviour referencji:

// C#
SomeFunction doSomething = someMethod;
doSomething();
doSomething = someOtherMethod;
doSomething();

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

Z anonimowym wewnętrznym klasy, można nawet uniknąć deklarowania oddzielnych nazwanych klas i prawie traktować je jak prawdziwe funkcje delegatów.

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

...

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

Powinno to być prawdopodobnie używane tylko wtedy, gdy implementacja jest bardzo specyficzna dla obecnego kontekstu i nie przyniesie korzyści z ponownego użycia.

I oczywiście w Javie 8 stają się one w zasadzie wyrażeniami lambda:

// Java 8
SomeMethod(() -> { /* your implementation */ });
 53
Author: Dave Cousineau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 01:32:58

Opowiadanie: nie .

Wprowadzenie

Najnowsza wersja środowiska programistycznego Microsoft Visual J++ obsługuje konstrukcję języka o nazwie lub metoda bound Bibliografia . Tej konstrukcji, oraz nowe słowa kluczowe delegate i multicast wprowadzone do obsługi, nie są częścią Java TM język programowania, który jest określony przez język Java Specyfikacja i zmienione przez specyfikację klasy wewnętrzne zawarte w dokumentacji dla oprogramowania JDKTM 1.1 .

Jest mało prawdopodobne, aby język programowania Java kiedykolwiek zawierał tej konstrukcji. Sun już dokładnie rozważał przyjęcie go w 1996 roku, w zakresie budowy i odrzucania pracujących prototypów. Nasze wniosek był taki, że odniesienia do metody wiązania są zbędne i szkodliwy dla języka. Decyzja ta została podjęta w konsultacji z Borland International, który miał wcześniejsze doświadczenia z bound odniesienia do metod w Delphi Object Pascal.

Uważamy, że odniesienia do metody bound są niepotrzebne , ponieważ inny design alternative, inner classes , provides equal or superior funkcjonalność. W szczególności klasy wewnętrzne w pełni wspierają wymagania dotyczące obsługi zdarzeń przez interfejs użytkownika i zostały wykorzystane do wdrożenie interfejsu użytkownika API co najmniej tak wszechstronnego jak Okna Zajęcia Fundacji.

Uważamy, że odniesienia do metody bound są szkodliwe, ponieważ umniejszają od prostoty języka programowania Java i wszechobecny obiektowy charakter API. Metoda wiązania odniesienia wprowadzają również nieprawidłowości w składni języka i zasady ustalania zakresu. Na koniec osłabiają inwestycje w technologie VM ponieważ maszyny wirtualne są wymagane do obsługi dodatkowych i różnych typów Bibliografia skutecznie.

 34
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 21:48:02

Czy czytałeś to :

Delegaty są użytecznym konstruktem w systemach opartych na zdarzeniach. Zasadniczo Delegaty są obiektami kodującymi metodę na określonym obiekt. Ten dokument pokazuje, w jaki sposób wewnętrzne klasy Javy zapewniają więcej ogólne rozwiązanie takich problemów.

Co To jest Delegat? Naprawdę jest bardzo podobny do wskaźnika do członka funkcja używana w C++. Ale delegat zawiera obiekt docelowy wraz z metodą, która ma być wywołana. Idealnie byłoby miło być potrafi powiedzieć:

Obj.registerHandler (ano.methodOne);

..i że metoda metodone będzie wywoływana na ano, gdy otrzymano jakieś konkretne zdarzenie.

To właśnie osiąga struktura delegatów.

Java Inner Classes

Twierdzono, że Java dostarcza tego funkcjonalności poprzez anonimowe klasy wewnętrzne i dlatego nie wymaga dodatkowych Konstruktor delegatów.

obj.registerHandler(new Handler() {
        public void handleIt(Event ev) {
            methodOne(ev);
        }
      } );
Na pierwszy rzut oka wydaje się to poprawne, ale jednocześnie uciążliwe. Ponieważ dla wielu przykładów przetwarzania zdarzeń prostota Składnia delegatów jest bardzo atrakcyjna.

General Handler

Jednakże, jeśli programowanie oparte na zdarzeniach jest używane w bardziej wszechobecny sposób, powiedzmy na przykład, jako część ogólnego asynchroniczne środowisko programowania, stawka jest większa.

W tak ogólnej sytuacji nie wystarczy włączyć tylko na metoda docelowa i instancja obiektu docelowego. Na ogół może być inne wymagane parametry, które są określane w kontekście, gdy opiekun wydarzenia jest zarejestrowany.

W tej bardziej ogólnej sytuacji, podejście java może zapewnić bardzo eleganckie rozwiązanie, szczególnie w połączeniu z wykorzystaniem finalnego zmienne:

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

Finał * finał * finał

Zwróciłeś uwagę?

Zauważ, że ostateczne zmienne są dostępne z poziomu anonimowego definicje metody klasowej. Pamiętaj, aby uważnie przestudiować ten kod, aby zrozum konsekwencje. Jest to potencjalnie bardzo potężny technika. Na przykład, może być stosowany do dobrego efektu podczas rejestracji obsługi w MiniDOM i w bardziej ogólnych sytuacjach.

Natomiast konstrukcja delegata nie zapewnia rozwiązania dla ten bardziej ogólny wymóg i jako taki powinien zostać odrzucony jako idiom, na którym można tworzyć projekty na podstawie.

 17
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-21 05:46:37

Wiem, że ten post jest stary, ale w Javie 8 dodano lambda i koncepcję funkcjonalnego interfejsu, czyli dowolnego interfejsu z jedną tylko metodą. Razem oferują one podobną funkcjonalność do C# delegatów. Zobacz tutaj, aby uzyskać więcej informacji, lub po prostu google Java Lambdas. http://cr.openjdk.java.net / ~briangoetz/lambda/lambda-state-final.html

 12
Author: user3311658,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-14 20:01:00

Nie, ale są fałszywe używając proxy i reflection:

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

Fajną rzeczą w tym idiomie jest to, że możesz sprawdzić, czy metoda delegowana-to istnieje i posiada wymagany podpis, w momencie tworzenia delegatora (choć niestety nie w czasie kompilacji, chociaż wtyczka FindBugs może tu pomóc), a następnie bezpiecznie użyć jej do delegowania do różnych instancji.

Zobacz kod karg na github aby uzyskać więcej testów oraz implementacja .

 4
Author: Dominic Fox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 18:10:10

Zaimplementowałem obsługę callback/delegate w Javie przy użyciu reflection. Szczegóły i robocze źródło są dostępne na mojej stronie .

Jak To Działa

Istnieje klasa zasad o nazwie Callback z zagnieżdżoną klasą o nazwie WithParms. API, które potrzebuje callback, weźmie obiekt Callback jako parametr i, jeśli jest to konieczne, utworzy Callback.WithParms jako zmienna metody. Ponieważ wiele zastosowań tego obiektu będzie rekurencyjnych, działa to bardzo czysto.

Ponieważ wydajność nadal jest dla mnie priorytetem, nie chciałem być zobowiązany do tworzenia tablicy obiektów, która przechowywałaby parametry dla każdego wywołania - w końcu w dużej strukturze danych może być tysiące elementów, a w scenariuszu przetwarzania wiadomości moglibyśmy skończyć przetwarzaniem tysięcy struktur danych na sekundę.

Aby była bezpieczna dla wątków, tablica parametrów musi istnieć unikalnie dla każdego wywołania metody API, a dla efektywności ten sam powinien być używany do każdego wywołania wywołania zwrotnego; potrzebowałem drugiego obiektu, który byłby Tani do wytworzenia, aby powiązać wywołanie zwrotne z tablicą parametrów dla wywołania. Ale w niektórych scenariuszach, Wywoływacz miałby już tablicę parametrów z innych powodów. Z tych dwóch powodów tablica parametrów nie należy do obiektu Callback. Również wybór wywołania (przekazywanie parametrów jako tablica lub jako poszczególne obiekty) należy do rąk API za pomocą wywołanie zwrotne umożliwiające użycie dowolnego wywołania najlepiej nadaje się do jego wewnętrznego działania.

Zagnieżdżona Klasa WithParms jest wtedy opcjonalna i służy dwóm celom, zawiera tablicę obiektu parametru potrzebną do wywołania wywołania zwrotnego oraz dostarcza 10 przeciążonych metod invoke () (z 1 do 10 parametrami), które ładują tablicę parametrów i wywołują obiekt wywołania zwrotnego.

Poniżej znajduje się przykład użycia wywołania zwrotnego do przetwarzania plików w drzewie katalogów. To jest to wstępna Walidacja, która liczy tylko pliki do przetworzenia i zapewnia, że żaden nie przekracza z góry określonego maksymalnego rozmiaru. W tym przypadku po prostu tworzymy callback inline z wywołaniem API. Jednak odzwierciedlamy metodę docelową jako wartość statyczną, aby odbicie nie było wykonywane za każdym razem.

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.processDirectory ():

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

Ten przykład ilustruje piękno tego podejścia - logika specyficzna dla aplikacji jest abstrakcyjna w wywołaniu zwrotnym, a żmudność rekurencyjnego chodzenia po drzewie katalogów jest ładnie schowana w całkowicie statycznej metodzie użyteczności wielokrotnego użytku. I nie musimy wielokrotnie płacić ceny definiowania i wdrażania interfejsu dla każdego nowego zastosowania. Oczywiście argumentem dla interfejsu jest to, że jest o wiele bardziej jednoznaczne co do tego, co należy zaimplementować (jest egzekwowane, a nie po prostu udokumentowane) - ale w praktyce nie znalazłem problemu, aby uzyskać prawidłową definicję wywołania zwrotnego.

Definiowanie i implementacja interfejsu nie jest tak zła (chyba że rozprowadzasz aplety, tak jak ja, gdzie unikanie tworzenia dodatkowych klas faktycznie ma znaczenie), ale to naprawdę świeci, gdy masz wiele wywołań zwrotnych w jednej klasie. Nie tylko jest zmuszony do wciskania ich każdego do osobnej klasy wewnętrznej dodanej narzutu w wdrożonej aplikacji, ale jest to wręcz żmudne do programowania i cały ten kod kotła-Płyta jest tak naprawdę po prostu "szum".

 1
Author: Lawrence Dol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 21:44:25

Choć nigdzie nie jest tak czyste, ale można zaimplementować coś w rodzaju C# delegatów używając Java Proxy .

 0
Author: John Meagher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-05 00:07:23

Nie, ale ma podobne zachowanie, wewnętrznie.

W C# delegaty są używane do tworzenia osobnego punktu wejścia i działają podobnie jak wskaźnik funkcji.

W Javie nie ma rzeczy jako wskaźnika funkcji (na górnym wyglądzie), ale wewnętrznie Java musi zrobić to samo, aby osiągnąć te cele.

Na przykład, tworzenie wątków w Javie wymaga klasy rozszerzającej wątek lub implementacji Runnable, ponieważ zmienna obiektu klasy może być używana w Miejscu Pamięci pointer.

 0
Author: Manasvi Sareen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-27 02:24:24

Tak i nie, ale wzorzec delegowania w Javie można by pomyśleć w ten sposób. Ten samouczek wideo jest o wymianie danych między fragmentami aktywności i ma wielką istotę wzorca delegatów przy użyciu interfejsów.

Interfejs Java

 0
Author: Khulja Sim Sim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-06 03:38:20

Java nie ma delegatów i jest z tego dumna:). Z tego co tu przeczytałem znalazłem w zasadzie 2 sposoby na udawanie delegatów: 1. odbicie; 2. Klasa wewnętrzna

Refleksje są sloooooow! Klasa wewnętrzna nie obejmuje najprostszej funkcji use-case: sort. Nie chcę wchodzić w szczegóły, ale rozwiązaniem z klasą wewnętrzną jest stworzenie klasy wrapper dla tablicy liczb całkowitych do posortowania w porządku rosnącym i klasy dla tablicy liczb całkowitych do posortowania w porządku malejącym.

 -11
Author: Leve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-06 08:23:59