2016-04-02 12 views
0

JavaFX ile Java 8'de EventHandler<ActionEvent>'un daha basit bir sürümünü sağlamaya çalışıyorum. onlar parametre olarak olayı yapmayız, onlar gizleme umurumda:Bir Lambda yöntemi ile olayı basitleştirme Java'da çalışmıyor

son hali Bununla

package dialogutil; 

import org.controlsfx.dialog.Dialog; 

import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 

@FunctionalInterface 
public interface Clickable extends EventHandler<ActionEvent> { 

    public static Clickable EMPTY =() -> {}; 

    public void onClick(); 

    @Override 
    public default void handle(ActionEvent event) { 
     this.onClick(); 
     if (event != null && event.getSource() != null) { 
      ((Dialog)event.getSource()).hide(); 
     } 
    } 
} 

gibi bakmak gerekiyordu, ben daha basit bir şekilde olay işleyicileri oluşturmak çalışıyorum kendilerini. Aşağıdaki bekliyoruz

İşte
package test; 

import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 

import org.junit.Test; 

public class BastelTest { 

    /** 
    * Interface Complicated is called with a value. 
    */ 
    @FunctionalInterface 
    interface Complicated { 
     void complicated(int value); 
    } 


    /** 
    * Interface Simple is called without a value. 
    */ 
    @FunctionalInterface 
    interface Simple extends Complicated { 
     void simple(); 
     /** 
     * The value given is printed and then the call is deflected to the simple method given. 
     */ 
     @Override 
     default void complicated(int value) { 
      System.out.println("Swallowing the " + value); 
      simple(); 
     } 
    } 

    /** 
    * This is in order to try the Complicated/Simple interface. 
    * The given {@link Complicated} is called with a 42. 
    * It can be a {@link Simple} as well; in this case the call is deflected. 
    * @param x 
    */ 
    private void callIt(Complicated x) { 
     x.complicated(42); 
    } 

    /** 
    * This is the interface I am indeed working on. 
    * Here the deflection doesn't work; instead, I get an AbstractMethodError. 
    */ 
    @FunctionalInterface 
    public interface Clickable extends EventHandler<ActionEvent> { 

     public static Clickable EMPTY =() -> {}; 

     public void onClick(); 

     @Override 
     public default void handle(ActionEvent event) { 
      System.out.println("Simplifying the call:"); 
      this.onClick(); 
      System.out.println("Call simplified."); 
     } 

    } 

    private void handle(EventHandler<ActionEvent> x) { 
     System.out.println("Handling null event via " + x); 
     x.handle(null); 
     System.out.println("Handling nonnull event via " + x); 
     x.handle(new ActionEvent()); 
    } 


    @Test 
    public void testFunc() { 
     callIt(x -> System.out.println("Complicated with " + x)); 
     callIt((Simple)() -> System.out.println("Called simple.")); 

     Clickable c =() -> System.out.println("Hdl3"); 
     c.handle(null); 

     handle(x -> System.out.println("Hdl1 " + x)); 
     handle((Clickable)() -> System.out.println("Hdl2")); 
     handle(Clickable.EMPTY); 
    } 
} 

gerçekleşmesi: gösteri uğruna

, ben bununla sahip sorunu yeniden bir test seti oluşturdu

  • ben callIt() ararsam ya İşleyici temel sürümü ile handle(), her zamanki gibi denir.
  • Onları "uzman", işleyici sınıfının basitleştirilmiş sürümüyle çağırırsam, bu çağrıyı verdiğim basitleştirilmiş sürümüne yönlendirmesini beklerim.

Bu kısmen çalışır: Simple baskı verilen bağımsız değişken bölgesinin complicated(int) yöntemini ve daha sonra da bir simple() yöntem olup, ifade çağırır: Simple/Complicated kombinasyonu ile

  • , çalışır bir lambda olarak. Bununla birlikte, bundan sonra olan kombinasyon, bir EventHandler<ActionEvent>'u, bir Clickable olan ve handle() bu onClick() olarak adlandırılan bir lambda (muhtemelen boş) olarak ifade etmektedir. (Lütfen isimler hakkında kafanız karışmayın; sadece geliştireceğim, fakat tamamen değişmeyecek, oldukça tarihi bir arayüz.) Bu durumda işe yaramıyor. İşte

yığın izleme alıyorum edilir:

java.lang.AbstractMethodError: Method test/BastelTest$$Lambda$7.handle(Ljavafx/event/Event;)V is abstract 
    at test.BastelTest$$Lambda$7/25282035.handle(Unknown Source) 
    at test.BastelTest.handle(BastelTest.java:38) 
    at test.BastelTest.testFunc(BastelTest.java:69) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at [usual test case stuff...] 

Neden çalışmıyor? Sıradan bir Java işlemleri kullanılarak

(Ben durum hakkında yeterince açık değil isem, bunu söyle.)

+1

Hatanızı JDK 1.8.0_74 ile yeniden oluşturamıyorum. Hangi versiyona sahipsin? Ayrıca, bu ilişkisizdir, ancak 'Basit bir şekilde Karmaşık' arabirimi 'gerçekten garip görünüyor (eğer basit bir şey karmaşıksa?) – Tunaki

+0

İlişkisiz. 'public static Clickable.EMPTY' - 'interface'lerdeki değişkenlere izin verilmez,' EMPTY 'dolaylı olarak' public static final 'olur. 'publicClick onClick()' burada 'public' gereksizdir. –

+0

1.8.0_20 sahibim. İyi fikir - Çalışıp çalışmadığını görmek için güncellemeliyim ... – glglgl

cevap

1

Genellikle, bir AbstractMethodError (LinkageError veya her türlü) kışkırtmak mümkün olmamalıdır. Bu durumla karşılaşmak, ya bir bozuk derleyicinin ya da uyumsuz olarak değiştirilmiş bir ortamın bir işaretidir, yani bir sınıf, derleme zamanında gördüğü sınıfın farklı bir versiyonuna karşı bağlanır.

Burada gördüğümüz, interface numaralı telefondan köprü yöntemi eksik. Bir jenerik tür ya da, bir alt değişken farklı bir alt değişken kullanarak bir tür değişkeni redeclaring bir türü veya bir tür genişletildiğinde, devralınan yöntemlerin ham tipi imzası değişebilir ve eski ham imzaya sahip bir köprü yöntemi gerektirebilir ve yöntemle temsilci yeni imza.Kodunuzda

, reifiable tip Clickable ikincisi, birincisine devrederek bir köprü yöntemi gerektiren genel tür EventHandler<ActionEvent> uzanır ve bayt kodu düzeyinde iki yöntem, void handle(ActionEvent) ve void handle(Event) sahiptir. Java 8 ile başlayarak

, bu köprü yöntemi (büyük ölçüde lambda ifadeleri için sınıflar oluşturma işlemini basitleştirmek) tüm uygulama sınıflarından uygulamadan yükünü çıkarılması, (artık olmayan abstract yöntemleri mümkündür gibi) interface uygulanan . biz yöntemi BastelTest.handle çiğ yöntemle handle(Ljavafx/event/Event;)V sona erecektir derleme zamanı tip EventHandler<ActionEvent> bir örneğinde handle yöntemi çağırmak için çalışıyor görebilirsiniz yığın izleme itibaren

, ancak gerekli köprü yöntemi için eksik lambda örneği, miras alması gereken arayüzde de eksik olduğunu ima eder.

Bağlantılı tüm sınıfların iç içe geçmiş sınıflar olduğu ve bu nedenle bir araya getirildiği, sizinki gibi bir test takımı için, eşleşmeyen sürümlerin (ancak imkansız olmasa bile) elde edilmesi pek olası değildir. Diğer olasılık, bug 436350, “Missing bridge method in interface results in AbstractMethodError”'u taşıyan eski bir Eclipse sürümünü kullanmanızdır.

+0

Wow, çok teşekkürler! Şu ana kadar a) biraz farklı bir yaklaşım kullanarak ilerledim ve b) koddaki diğer noktalar üzerinde çalışıyorum, bu çok kapsamlı cevabı gerçekten takdir ediyorum. Zaman kısıtlamaları nedeniyle, Eclipse'nin "suçlu" olup olmadığını şu anda test edemiyorum. Ancak, önümüzdeki hafta mümkün olan en kısa sürede - muhtemelen önümüzdeki haftaya bakacağım. – glglgl