Java'daki İşlev İşaretçileri


148

Bu yaygın ve önemsiz bir şey olabilir, ancak somut bir cevap bulmakta zorlanıyorum. C # 'da, C ++' dan gelen fonksiyon işaretçileri fikri ile güçlü bir şekilde ilgili olan bir temsilci kavramı vardır. Java'da benzer bir işlevsellik var mı? İşaretçilerin bir şekilde eksik olduğu göz önüne alındığında, bunun için en iyi yol nedir? Ve net olmak gerekirse, burada birinci sınıftan bahsediyoruz.


1
Sadece dinleyicilerin veya diğer OOP yapılarının nesne görevlerini korurken aynı görevi yapmasını neden istediğinizi merak ediyorum. Demek istediğim, kavramın sunduğu işleve duyulan ihtiyacın sadece düz bir nesne ile elde edilebileceğini anlıyorum. :-)
Newtopian

2
Java 8'in lambda ifadeleri vardır: docs.oracle.com/javase/tutorial/java/javaOO/… Bunu kontrol etmek isteyebilirsiniz. Bir işlev işaretçisi değil, ancak yine de daha yararlı olabilir.
SinirliWithFormsDesigner

6
Java 8 yöntem başvuruları tam olarak istediğiniz şeydir.
Steven

8
Java 8 yöntem başvuruları tam olarak istediğiniz şeydir. this::myMethodanlamsal olarak lambda oluşturmakla aynıdır paramA, paramB -> this.myMethod(paramA, paramB).
Steven

Yanıtlar:


126

İşlev işaretçisi benzeri işlevler için Java deyimi, bir arabirimi uygulayan anonim bir sınıftır;

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

Güncelleme: yukarıdakiler, Java 8'den önceki Java sürümlerinde gereklidir. Şimdi daha güzel alternatiflerimiz var: lambdas:

list.sort((a, b) -> a.isGreaterThan(b));

ve yöntem referansları:

list.sort(MyClass::isGreaterThan);

2
Strateji tasarım deseni. İşlevin bağımsız değişken olarak sağlanmayan bazı küresel olmayan verileri kullanmasını istediğinizde, C veya C ++ işlev işaretçisi kadar çirkin değildir.
Raedwald

75
@Raedwald C ++ 'da işleçler vardır ve C ++ 0x'de işleçlerin üzerine kurulmuş kapaklar ve lambdalar vardır. std::bindParametreleri işlevlere bağlayan ve çağrılabilir bir nesne döndüren bile vardır . Bu gerekçelerle C'yi savunamıyorum, ancak C ++ bunun için Java'dan daha iyi.
zneak

21
@zneak, @Raedwald: Bunu, bir işaretçi boyunca kullanıcı verilerine ileterek (örn. struct) C'de yapabilirsiniz. (ve her yeni geri arama dolaylaması düzeyi ile kapsül içine alabilir) Aslında oldukça temiz.
brice

25
Evet, aslında C fonksiyon göstergelerini acımasız ambalaj sınıfları üzerinden her gün alacağım
BT

3
Java 8 bunun için daha iyi bir sözdizimine sahiptir.
Thorbjørn Ravn Andersen

65

Bir işlev işaretçisini bir arabirimle değiştirebilirsiniz. Diyelim ki bir koleksiyondan geçmek ve her bir elemanla bir şeyler yapmak istiyorsunuz.

public interface IFunction {
  public void execute(Object o);
}

Bu bazı derleme CollectionUtils2.doFunc (Koleksiyon c, IFunction f) geçebilir arayüz.

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

Örnek olarak, bir sayı koleksiyonumuz olduğunu ve her öğeye 1 eklemek istediğinizi varsayalım.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});

42

Bunu yapmak için yansımayı kullanabilirsiniz.

Parametre olarak nesneyi ve yöntem adını (dize olarak) iletin ve sonra yöntemi çağırın. Örneğin:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

Ve sonra şu şekilde kullanın:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

Tabii ki, tüm istisnaları kontrol edin ve gerekli kadroları ekleyin.


7
Yansıma "Yavaş tehlikeli java kodu nasıl yazarım" dışında hiçbir şey için doğru cevap değildir: D
Jacob

5
"Çirkin" ve yavaş olabilir, ancak yansımanın kabul edilen cevaptaki arayüz tabanlı çözümün yapamayacağı şeylere izin verdiğine inanıyorum (yanlış olmadıkça), yani aynı nesnenin aynı kod içinde çeşitli yöntemlerini çağırın bölümü.
Eusebius

@zoquete .. Ben bu yazı geçiyordu ve bir şüphe vardı .. bu yüzden yaklaşımınızda, eğer "theDescription" değişken erişirseniz, fonksiyon her çağrılacak ve değişken her erişilir ??
karthik27

20

Hayır, işlevler java'daki birinci sınıf nesneler değildir. Aynı şeyi bir işleyici sınıfı uygulayarak da yapabilirsiniz - geri dönüşler Swing'de bu şekilde uygulanır.

Bununla birlikte, java'nın gelecek sürümlerinde kapatma önerileri (bahsettiğiniz şeyin resmi adı) var - Javaworld'ün ilginç bir makalesi var.


15

Bu, Steve Yegge'nin İsimler Krallığı'ndaki infazını akla getiriyor . Temel olarak Java'nın her eylem için bir nesneye ihtiyaç duyduğunu ve bu nedenle işlev işaretçileri gibi "yalnızca fiil" varlıklara sahip olmadığını belirtir.


8

Benzer işlevsellik elde etmek için anonim iç sınıfları kullanabilirsiniz.

Bir arayüz tanımlayacaksanız Foo:

interface Foo {
    Object myFunc(Object arg);
}

Bağımsız bardeğişken olarak 'işlev işaretçisi' alacak bir yöntem oluşturun :

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

Son olarak yöntemi aşağıdaki gibi çağırın:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}

7

Java8 lambdas ve yöntem referanslarını tanıttı . Dolayısıyla, işleviniz işlevsel bir arabirimle eşleşiyorsa (kendinizinkini oluşturabilirsiniz) bu durumda bir yöntem başvurusu kullanabilirsiniz.

Java bir dizi ortak fonksiyonel arabirim sağlar . oysa aşağıdakileri yapabilirsiniz:

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

bir kavram var mı alias: eg public List<T> collect(T t) = Collections::collect
javadba

@javadba bildiğim kadarıyla.
Alex

5

Java'da böyle bir şey yoktur. Başvuruyu bir nesneye sarmanız ve referansı o nesnedeki yönteme aktarmak için o nesneye başvurmanız gerekir.

Sözdizimsel olarak, bu, yerinde tanımlanan anonim sınıfları veya sınıfın üye değişkenleri olarak tanımlanan anonim sınıfları kullanarak belirli bir ölçüde kolaylaştırılabilir.

Misal:

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

Yansıma kullanarak Java'da geri arama / temsilci desteği uyguladım. Ayrıntılar ve çalışma kaynağı web sitemde mevcuttur .

Nasıl çalışır

WithParms adında bir iç içe sınıf ile Geri Arama adlı bir ilke sınıfımız var. Geri aramaya ihtiyaç duyan API, parametre olarak bir Callback nesnesini alır ve gerekirse bir yöntem değişkeni olarak bir Callback.WithParms oluşturur. Bu nesnenin birçok uygulaması özyinelemeli olacağından, bu çok temiz çalışır.

Performans hala benim için yüksek bir öncelik ile, her çağırma için parametreleri tutmak için bir ıskarta nesne dizisi oluşturmak için gerekli değildi - sonuçta büyük bir veri yapısında binlerce öğe ve bir mesaj işleme olabilir senaryo saniyede binlerce veri yapısını işleyebiliriz.

Thread güvenli olması için, parametre dizisinin API yönteminin her çağrılması için benzersiz bir şekilde mevcut olması gerekir ve verimlilik için, geri çağrının her çağrılması için aynı dizinin kullanılması gerekir; Ben geri çağırma çağırma için bir parametre dizisi ile bağlamak için oluşturmak için ucuz olurdu ikinci bir nesne gerekiyordu. Ancak, bazı senaryolarda, invoker zaten başka nedenlerle bir parametre dizisine sahip olacaktır. Bu iki nedenden ötürü, parametre dizisi Geri Arama nesnesine ait değildi. Ayrıca, invokasyon seçimi (parametreleri bir dizi veya tek tek nesneler olarak geçirme), iç çalışmalarına en uygun invokasyonun hangisinin uygun olduğunu kullanmasını sağlayan geri arama özelliğini kullanarak API'nın elindedir.

WithParms nested class, o zaman isteğe bağlıdır ve iki amaca hizmet eder, geri arama çağrıları için gereken parametre nesnesi dizisini içerir ve parametre dizisini yükleyen ve daha sonra 10 aşırı yüklenmiş invoke () yöntemi (1'den 10 parametreye kadar) sağlar ve geri arama hedefini çağırır.



-1

Buradaki çoğu insanla ilgili olarak java konusunda yeniyim ama benzer bir öneri görmediğim için önerecek başka bir alternatifim var. Ben iyi bir uygulama olup olmadığını emin değilim, hatta daha önce önerdi ve ben sadece alamadım. Kendini tanımlayıcı olduğunu düşündüğüm için beğendim.

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

sonra uygulamaya bağlı olarak,

 functionpointer = new Function1();
 functionpointer = new Function2();

vb.

ve arayarak

 functionpointer.execute();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.