JavaFX - 如何在 Kotlin 中使用 MapValueFactory?
JavaFX - How do you use MapValueFactory in Kotlin?
我正在尝试在 Kotlin 的 TableView 上使用 MapValueFactory。我在 https://jenkov.com/tutorials/javafx/tableview.html
的 Java 中找到了一个很好的例子
以下 Java 代码工作正常:
package com.example.tableview;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
var tableView = new TableView();
TableColumn<Map, String> firstNameColumn = new TableColumn<>("firstName");
firstNameColumn.setCellValueFactory(new MapValueFactory<>("firstName"));
TableColumn<Map, String> lastNameColumn = new TableColumn<>("lastName");
lastNameColumn.setCellValueFactory(new MapValueFactory<>("lastName"));
tableView.getColumns().add(firstNameColumn);
tableView.getColumns().add(lastNameColumn);
ObservableList<Map<String, Object>> items = FXCollections.<Map<String, Object>>observableArrayList();
Map<String, Object> item1 = new HashMap<>();
item1.put("firstName", "Randall");
item1.put("lastName" , "Kovic");
items.add(item1);
Map<String, Object> item2 = new HashMap<>();
item2.put("firstName", "Irmelin");
item2.put("lastName" , "Satoshi");
items.add(item2);
tableView.getItems().addAll(items);
Scene scene = new Scene(tableView, 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
当我将该代码剪切并粘贴到 IntelliJ 中的 Kotlin 项目中时,它会尝试从 Java 自动转换为 Kotlin。但是,生成的代码不会编译。它说有一个类型不匹配试图将 firstNameColumn 添加到 tableView.columns.
Type mismatch.
Required: Nothing!
Found: TableColumn<Map<*, *>, String>
这里是生成的 Kotlin 代码,它不会编译:
package com.example.tableview
import javafx.application.Application
import javafx.collections.FXCollections
import javafx.scene.Scene
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
import javafx.scene.control.cell.MapValueFactory
import javafx.stage.Stage
class HelloApplication : Application() {
override fun start(stage: Stage) {
val tableView: TableView<*> = TableView<Any?>()
val firstNameColumn = TableColumn<Map<*, *>, String>("firstName")
firstNameColumn.setCellValueFactory(MapValueFactory("firstName"))
val lastNameColumn = TableColumn<Map<*, *>, String>("lastName")
lastNameColumn.setCellValueFactory(MapValueFactory("lastName"))
tableView.columns.add(firstNameColumn)
tableView.columns.add(lastNameColumn)
val items = FXCollections.observableArrayList<Map<String, Any>>()
val item1: MutableMap<String, Any> = HashMap()
item1["firstName"] = "Randall"
item1["lastName"] = "Kovic"
items.add(item1)
val item2: MutableMap<String, Any> = HashMap()
item2["firstName"] = "Irmelin"
item2["lastName"] = "Satoshi"
items.add(item2)
tableView.items.addAll(items)
val scene = Scene(tableView, 320.0, 240.0)
stage.title = "Hello!"
stage.scene = scene
stage.show()
}
fun main() {
Application.launch(HelloApplication::class.java)
}
}
如有任何帮助,我们将不胜感激。为了让类型正确,我已经深入研究了几个老鼠洞...
编译器错误
您的编译器错误来自于您将 tableView
设为 TableView<*>
。它对类型的了解还不够多,无法让您向列列表中添加任何内容。只需更改此行:
val tableView: TableView<*> = TableView<Any?>()
以下之一:
val tableView: TableView<Map<*, *>> = TableView()
// or
val tableView = TableView<Map<*, *>>()
会让您的 Kotlin 代码编译,并且 运行 符合预期。
您的 Java 代码有问题
您的 Java 代码可以编译,因为您在那里创建了 tableView
原始类型 TableView
。这意味着没有使用与 TableView
关联的泛型。除非您与早于 Java 5.
的遗留代码进行交互,否则您不应该使用原始类型
你应该改变:
var tableView = new TableView();
收件人:
var tableView = new TableView<Map<?, ?>>();
请注意,您在参数化 TableColumn
时也使用了原始类型。修复是类似的,但不幸的是它破坏了使用 MapValueFactory
的能力,因为它在实现 Callback
时声明了原始类型。真正的解决方法是不使用 MapValueFactory
(见下文)。
不要使用 *ValueFactory
类
类 诸如 PropertyValueFactory
和 MapValueFactory
是在 lambda 表达式(在 Java 中)不是东西的时候添加的。这意味着使用匿名 class。匿名 classes 可能非常冗长,因此他们添加了方便的 classes 以使开发人员更容易编写代码。但它们有两个缺点:它们依赖于反射,更重要的是,你失去了所有 compile-time 验证(例如,属性 是否存在,属性 的类型等)。
在 Java 和 Kotlin 中,我建议使用 lambda 表达式(同时也不要使用原始类型)。
Java:
// Note everywhere is using a Map<String, String> now
var tableView = new TableView<Map<String, String>>();
TableColumn<Map<String, String>, String> firstNameColumn = new TableColumn<>("firstName");
firstNameColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().get("firstName")));
TableColumn<Map<String, String>, String> lastNameColumn = new TableColumn<>("lastName");
lastNameColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().get("lastName")));
科特林:
// Again, note everywhere is using Map<String, String>
val tableView = TableView<Map<String, String>>()
val firstNameColumn = TableColumn<Map<String, String>, String>("firstName")
firstNameColumn.setCellValueFactory { SimpleStringProperty(it.value["firstName"]) }
val lastNameColumn = TableColumn<Map<String, String>, String>("lastName")
lastNameColumn.setCellValueFactory { SimpleStringProperty(it.value["lastName"]) }
虽然我在两个版本中都使用了 Map<String, String>
,但您显然可以根据需要对 Map
进行不同的参数设置。
我正在尝试在 Kotlin 的 TableView 上使用 MapValueFactory。我在 https://jenkov.com/tutorials/javafx/tableview.html
的 Java 中找到了一个很好的例子以下 Java 代码工作正常:
package com.example.tableview;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
var tableView = new TableView();
TableColumn<Map, String> firstNameColumn = new TableColumn<>("firstName");
firstNameColumn.setCellValueFactory(new MapValueFactory<>("firstName"));
TableColumn<Map, String> lastNameColumn = new TableColumn<>("lastName");
lastNameColumn.setCellValueFactory(new MapValueFactory<>("lastName"));
tableView.getColumns().add(firstNameColumn);
tableView.getColumns().add(lastNameColumn);
ObservableList<Map<String, Object>> items = FXCollections.<Map<String, Object>>observableArrayList();
Map<String, Object> item1 = new HashMap<>();
item1.put("firstName", "Randall");
item1.put("lastName" , "Kovic");
items.add(item1);
Map<String, Object> item2 = new HashMap<>();
item2.put("firstName", "Irmelin");
item2.put("lastName" , "Satoshi");
items.add(item2);
tableView.getItems().addAll(items);
Scene scene = new Scene(tableView, 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
当我将该代码剪切并粘贴到 IntelliJ 中的 Kotlin 项目中时,它会尝试从 Java 自动转换为 Kotlin。但是,生成的代码不会编译。它说有一个类型不匹配试图将 firstNameColumn 添加到 tableView.columns.
Type mismatch.
Required: Nothing!
Found: TableColumn<Map<*, *>, String>
这里是生成的 Kotlin 代码,它不会编译:
package com.example.tableview
import javafx.application.Application
import javafx.collections.FXCollections
import javafx.scene.Scene
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
import javafx.scene.control.cell.MapValueFactory
import javafx.stage.Stage
class HelloApplication : Application() {
override fun start(stage: Stage) {
val tableView: TableView<*> = TableView<Any?>()
val firstNameColumn = TableColumn<Map<*, *>, String>("firstName")
firstNameColumn.setCellValueFactory(MapValueFactory("firstName"))
val lastNameColumn = TableColumn<Map<*, *>, String>("lastName")
lastNameColumn.setCellValueFactory(MapValueFactory("lastName"))
tableView.columns.add(firstNameColumn)
tableView.columns.add(lastNameColumn)
val items = FXCollections.observableArrayList<Map<String, Any>>()
val item1: MutableMap<String, Any> = HashMap()
item1["firstName"] = "Randall"
item1["lastName"] = "Kovic"
items.add(item1)
val item2: MutableMap<String, Any> = HashMap()
item2["firstName"] = "Irmelin"
item2["lastName"] = "Satoshi"
items.add(item2)
tableView.items.addAll(items)
val scene = Scene(tableView, 320.0, 240.0)
stage.title = "Hello!"
stage.scene = scene
stage.show()
}
fun main() {
Application.launch(HelloApplication::class.java)
}
}
如有任何帮助,我们将不胜感激。为了让类型正确,我已经深入研究了几个老鼠洞...
编译器错误
您的编译器错误来自于您将 tableView
设为 TableView<*>
。它对类型的了解还不够多,无法让您向列列表中添加任何内容。只需更改此行:
val tableView: TableView<*> = TableView<Any?>()
以下之一:
val tableView: TableView<Map<*, *>> = TableView()
// or
val tableView = TableView<Map<*, *>>()
会让您的 Kotlin 代码编译,并且 运行 符合预期。
您的 Java 代码有问题
您的 Java 代码可以编译,因为您在那里创建了 tableView
原始类型 TableView
。这意味着没有使用与 TableView
关联的泛型。除非您与早于 Java 5.
你应该改变:
var tableView = new TableView();
收件人:
var tableView = new TableView<Map<?, ?>>();
请注意,您在参数化 TableColumn
时也使用了原始类型。修复是类似的,但不幸的是它破坏了使用 MapValueFactory
的能力,因为它在实现 Callback
时声明了原始类型。真正的解决方法是不使用 MapValueFactory
(见下文)。
不要使用 *ValueFactory
类
类 诸如 PropertyValueFactory
和 MapValueFactory
是在 lambda 表达式(在 Java 中)不是东西的时候添加的。这意味着使用匿名 class。匿名 classes 可能非常冗长,因此他们添加了方便的 classes 以使开发人员更容易编写代码。但它们有两个缺点:它们依赖于反射,更重要的是,你失去了所有 compile-time 验证(例如,属性 是否存在,属性 的类型等)。
在 Java 和 Kotlin 中,我建议使用 lambda 表达式(同时也不要使用原始类型)。
Java:
// Note everywhere is using a Map<String, String> now
var tableView = new TableView<Map<String, String>>();
TableColumn<Map<String, String>, String> firstNameColumn = new TableColumn<>("firstName");
firstNameColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().get("firstName")));
TableColumn<Map<String, String>, String> lastNameColumn = new TableColumn<>("lastName");
lastNameColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().get("lastName")));
科特林:
// Again, note everywhere is using Map<String, String>
val tableView = TableView<Map<String, String>>()
val firstNameColumn = TableColumn<Map<String, String>, String>("firstName")
firstNameColumn.setCellValueFactory { SimpleStringProperty(it.value["firstName"]) }
val lastNameColumn = TableColumn<Map<String, String>, String>("lastName")
lastNameColumn.setCellValueFactory { SimpleStringProperty(it.value["lastName"]) }
虽然我在两个版本中都使用了 Map<String, String>
,但您显然可以根据需要对 Map
进行不同的参数设置。