簡體   English   中英

使用Lambda方法簡化事件在Java中不起作用

[英]Simplifying event with a Lambda method doesn't work in Java

我正在嘗試使用JavaFX在Java 8中提供EventHandler<ActionEvent>的簡單版本。

最終版本應該看起來像

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

以此,我試圖以一種更簡單的方式創建事件處理程序:它們不將事件作為參數,並且他們關心隱藏自己。

為了演示,我創建了一個測試套件來重現我遇到的問題:

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

我希望在這里發生以下情況:

  • 如果我使用處理程序的基本版本調用callIt()handle() ,則照常調用它。
  • 如果我使用處理程序類的“專用”簡化版本來調用它們,則我希望它會將調用轉移到我給出的簡化版本上。

這僅部分起作用:

  • Simple / Complicated組合一起使用時,它的工作原理是:調用Simple complex complicated(int)方法將打印給定參數,然后調用simple()方法,該方法又表示為lambda。
  • 但是,我所采用的組合是將EventHandler<ActionEvent>為lambda(可能甚至為空),該lambda形成一個Clickablehandle()稱為此onClick() (請不要對名稱感到困惑;這是一個非常有歷史意義的界面,我將要改進它,但不能完全更改。)在這種情況下,它不起作用。

這是我得到的堆棧跟蹤:

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...]

為什么不起作用?

(如果我對這種情況不夠清楚,請告訴我。)

通常,您不應使用普通的Java操作來引發AbstractMethodError (或任何類型的LinkageError )。 因此遇到它通常是編譯器損壞或環境發生不兼容變化的跡象,即,一個類鏈接到與編譯時所看到的不同版本的類。

我們在這里看到的是interface缺少的橋接方法 當泛型類型通過可更改類型或使用不同下限重新聲明類型變量的類型擴展時,繼承方法的原始類型簽名可能會更改,並且需要具有舊原始簽名並委托給該方法的橋方法新的簽名。

在您的代碼中,可更改類型Clickable擴展了泛型類型EventHandler<ActionEvent>並且在字節代碼級別上具有兩個方法,即void handle(ActionEvent)void handle(Event) ,后者需要委托前者的橋接方法。

從Java 8開始,這些橋接方法在interface中實現(因為現在可以使用非abstract方法),從而減輕了從所有實現類中實現它的負擔(大大簡化了為lambda表達式生成類的過程)。

從堆棧跟蹤中可以看到, BastelTest.handle方法正在嘗試在具有編譯時類型EventHandler<ActionEvent>的實例上調用handle方法,該方法最終將在原始方法handle(Ljavafx/event/Event;)V ,但是lambda實例缺少必需的bridge方法,這意味着在應該繼承它的接口中也缺少它。

對於像您這樣的測試套件,其中所有鏈接的類都是嵌套類,因此可以一起編譯,因此不太可能獲得不匹配的版本(盡管並非不可能)。 另一種可能性是您正在使用帶有錯誤436350,“接口結果中的橋梁方法缺少AbstractMethodError”的早期Eclipse版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM