【问题标题】:Use of final keyword in case of objects在对象的情况下使用 final 关键字
【发布时间】:2013-09-20 04:28:57
【问题描述】:

我知道如何使用 final 关键字。假设如果将一个类设为 final 那么它不能被继承,如果一个方法是 final 那么它不能被覆盖,如果一个变量是 final 那么它的值不能被改变.但是在这种情况下我有点困惑

    final TextField urlTextField = new TextField();

我认为如果 urlTextField 是最终的,那么你不能再做一次

urlTextField=new textField()。但在下面的例子中为什么它是最终的

Source

package org.carlfx;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Worker.State;
import javafx.print.*;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.transform.Scale;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

/**
 * Demo to use JavaFX 8 Printer API.
 *
 * @author cdea
 */
public class PrintDemo extends Application {
    @Override
    public void start(Stage primaryStage) {

        final TextField urlTextField = new TextField();
        final Button printButton = new Button("Print");
        final WebView webPage = new WebView();
        final WebEngine webEngine = webPage.getEngine();

        HBox hbox = new HBox();
        hbox.getChildren().addAll(urlTextField, printButton);
        BorderPane borderPane = new BorderPane();
        borderPane.setTop(hbox);
        borderPane.setCenter(webPage);
        Scene scene = new Scene(borderPane, 300, 250);
        primaryStage.setTitle("Print Demo");
        primaryStage.setScene(scene);

        // print button pressed, page loaded
        final BooleanProperty printButtonClickedProperty = new SimpleBooleanProperty(false);
        final BooleanProperty pageLoadedProperty = new SimpleBooleanProperty(false);

        // when the a page is loaded and the button was pressed call the print() method.
        final BooleanProperty printActionProperty = new SimpleBooleanProperty(false);
        printActionProperty.bind(pageLoadedProperty.and(printButtonClickedProperty));

        // WebEngine updates flag when finished loading web page.
        webEngine.getLoadWorker()
                 .stateProperty()
                 .addListener( (ChangeListener) (obsValue, oldState, newState) -> {
                    if (newState == State.SUCCEEDED) {
                        pageLoadedProperty.set(true);
                    }
                 });

        // When user enters a url and hits the enter key.
        urlTextField.setOnAction( aEvent ->  {
            pageLoadedProperty.set(false);
            printButtonClickedProperty.set(false);
            webEngine.load(urlTextField.getText());
        });

        // When the user clicks the print button the webview node is printed
        printButton.setOnAction( aEvent -> {
            printButtonClickedProperty.set(true);
        });

        // Once the print action hears a true go print the WebView node.
        printActionProperty.addListener( (ChangeListener) (obsValue, oldState, newState) -> {
            if (newState) {
                print(webPage);
            }
        });

        primaryStage.show();

    }

    /** Scales the node based on the standard letter, portrait paper to be printed.
     * @param node The scene node to be printed.
     */
    public void print(final Node node) {
        Printer printer = Printer.getDefaultPrinter();
        PageLayout pageLayout = printer.createPageLayout(Paper.NA_LETTER, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT);
        double scaleX = pageLayout.getPrintableWidth() / node.getBoundsInParent().getWidth();
        double scaleY = pageLayout.getPrintableHeight() / node.getBoundsInParent().getHeight();
        node.getTransforms().add(new Scale(scaleX, scaleY));

        PrinterJob job = PrinterJob.createPrinterJob();
        if (job != null) {
            boolean success = job.printPage(node);
            if (success) {
                job.endJob();
            }
        }
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

【问题讨论】:

  • 通过使用final 声明该变量不能更改(通过重新分配)。有什么问题?
  • 在这种情况下使用final是为了允许嵌套类/闭包访问对象(即start方法中的事件处理程序)
  • @MadProgrammer 不是类 - 闭包(可能翻译为内部匿名类);)
  • @alfasin 不是 FX 开发人员 - 抱歉,我很淘气:P
  • @MadProgrammer 它与 FX 无关 - 我认为 Java 8 开始支持闭包,所以我们很快就会知道它是什么 :)

标签: java java-8


【解决方案1】:

在 Java 7 之前,您需要声明一个局部变量 final 才能从内部类访问它。

从 Java 8 开始不再需要此功能:the variable only need to be effectively final(即未更改)。该更改与 lambda 语法保持一致。

所以从技术上讲,如果您使用的是 Java FX 8,则此处不需要 final 关键字。

【讨论】:

  • 你在这里写的所有东西 - 在上面的答案+cmets中已经提到过。
  • @assylias 感谢您的回答,这是学习的好点+1。好吧,我有点困惑。您说“直到 Java 7,您需要声明一个局部变量 final 才能从内部类访问它".here 在我的代码中,这是内部类。
  • @javaBeginner 我有似曾相识的感觉 :) stackoverflow.com/questions/18908823/…
  • @alfasin 您的回答指出“尝试从声明中删除 final,您会看到在第 4 行会出现编译器错误”,这与 Java 8(用于这个问题)所以我认为发布更准确的答案会很有用。
  • @assylias 你是对的 - 即使支持闭包,Java 7 也不支持 lambda 表达式 (->)。打算更新我的答案
【解决方案2】:

在 Java 7 中,当您在匿名类中并尝试使用/接近包装类的变量时 - 该变量应声明为 final ,否则编译器会报错。同样的事情也适用于 Lambda 表达式(受 Java 8 支持)。尝试从声明中删除final,您将在第 4 行收到编译器错误:

    urlTextField.setOnAction( aEvent ->  {
        pageLoadedProperty.set(false);
        printButtonClickedProperty.set(false);
        webEngine.load(urlTextField.getText()); // <-- compile error 
    });  

来自documentation

匿名类不能访问其封闭的局部变量 未声明为最终或有效最终的范围。

Lambda expressions 和匿名类具有相似的属性(可以将 lambda 表达式定义为两件事的组合:代码和范围)。除此之外,与内部类一样,Lambda 表达式只能使用在 lambda 之外声明的 final(或“有效最终”)变量。以下是来自 documentation 的示例:

        // The following statement causes the compiler to generate
        // the error "local variables referenced from a lambda expression
        // must be final or effectively final" in statement A:
        //
        // x = 99;

        Block<Integer> myBlock = (y) -> 
        {
            System.out.println("x = " + x); // Statement A
            System.out.println("y = " + y);
            System.out.println("this.x = " + this.x);
            System.out.println("LambdaScopeTest.this.x = " +
                LambdaScopeTest.this.x);
        };

【讨论】:

  • 在给定的代码中,内部类是什么,我也没有创建任何匿名类。
  • aEvent -&gt; {... 开头的代码正在创建一个闭包。在我的回答的最后一段中提到了。
  • 如果我没记错(他们没有改变它),那么当变量是“有效最终的”(即你不需要final,但你必须遵守它的大部分规则)。
  • @JoachimSauer 不是我回答中的最后一个链接;)
  • @assylias 对,它必须是最终的或有效的最终的,这意味着它不是最终的,但我们仍然无法在 lambda 的主体内更改它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
相关资源
最近更新 更多