JavaFx 使用反射和自定义注释创建 TableColumn : can't create instance of parameter typed class

标签 java generics reflection javafx annotations

为了便于在 JavaFX 应用程序中创建包含列的 TableView,我考虑创建一个注释来指示模型类中的字段,我希望创建一个具有与其关联的单元格值类型的 TableColumn。这是我写的注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AsTableColumn {

    String text();
    int index();
}

还有一个示例模型类 Person,如下所示:

public class Person {
    @AsTableColumn(text="Name", index=0)
    private String name;
    @AsTableColumn(text="Age", index=0)
    private int age;

    // setters and getters
}

在 JavaFX 中创建带有列的 TableView 的 native 方式中,我必须编写如下代码:

TableView<Person> table = new TableView<>();

TableColumn<Person, String> nameCol = new TableColumn<Person, String>("Name");
TableColumn<Person, Integer> ageCol = new TableColumn<Person, Integer>("Age");

nameCol.setCellValueFactory(cellData -> new SimpleObjectProperty<String>(cellData.getValue().getName()));
ageCol.setCellValueFactory(cellData -> new SimpleObjectProperty<Integer>(cellData.getValue().getAge()));

我有几个包含很多列的表,因此编写大量这样的代码很烦人,我认为应该有一种方法来简化它。

我在写注解处理器的时候遇到了一个关于创建TableColumn<T,S>的问题T 在方法签名中参数化,但 S 在运行时动态检测的实例。

import java.lang.reflect.Field;

import javafx.scene.control.TableColumn;

public class AnnotationProcessor {

    /**
     * Process the model class. 
     * 
     * For the fields declared which have annotation AsTableColumn, create a TableColumn<T,S>
     * instance for it with the detected field type. 
     * @param clas
     */
    public static <T> List<TableColumn<T, ? extends Object>> process(Class<T> clas) {

        for(Field field : clas.getDeclaredFields()) {
            if(field.isAnnotationPresent(AsTableColumn.class)) {
                AsTableColumn anno = field.getAnnotation(AsTableColumn.class);
                Class<?> type = field.getType();

                TableColumn<T, /* how to specify the field type here???*/> col = new TableColumn<>();
            }
        }
    }
}

最佳答案

您可以创建一个方法

private static <T,S> TableColumn<T,S> createTableColumn(Class<S> type, Field field, String text) {
    TableColumn<T, S> col =  new TableColumn<T,S>(text);
    col.setCellValueFactory(cellData -> {
        try {
            boolean wasAccessible = field.isAccessible() ;
            field.setAccessible(true);
            @SuppressWarnings("unchecked")
            SimpleObjectProperty<S> property = new SimpleObjectProperty<S>((S)(field.get(cellData.getValue())));
            field.setAccessible(wasAccessible);
            return property;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    });
    return col ;
}

然后就这样做

TableColumn<T, ?> col = createTableColumn(type, field, anno.text());

不过,传递类型是否能给您带来很多好处还不是很清楚。我认为你可以这样做:

AsTableColumn anno = field.getAnnotation(AsTableColumn.class);
TableColumn<T, Object> col = new TableColumn<>(anno.text());
col.setCellValueFactory(cellData -> {
    try {
        boolean wasAccessible = field.isAccessible() ;
        field.setAccessible(true);
        SimpleObjectProperty<Object> property = new SimpleObjectProperty<>(field.get(cellData.getValue()));
        field.setAccessible(wasAccessible);
        return property;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

});

这本质上是@Thomas 发布(并删除)的解决方案。

FWIW,我通常只使用静态实用方法来创建表列:

private static <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
    TableColumn<S,T> col = new TableColumn<>(text);
    col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
    return col ;
}

然后你就可以了

table.getColumns().add(column("Name", Person::nameProperty));
table.getColumns().add(column("Age", Person::ageProperty));

如果您不使用 JavaFX 属性,则可以很容易地将其调整为 private static <S,T> TableColumn<S,T> column(String text, Function<S,T> property); ,但我认为您必须为原始类型制作特殊情况。

关于JavaFx 使用反射和自定义注释创建 TableColumn : can't create instance of parameter typed class,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35848332/

相关文章:

java - 在 ListView 中设置ImageDrawable

java - 使用 JLink JRE 进行 Spring Boot

java - 使用可变参数实现泛型方法

C# 泛型和推断类型

c# - 需要反射帮助 : GetProperty Returns Unexpected Type

java - 如何安装 ActiveAndroid 以便与 Eclipse 一起使用?

java - ZK 应用程序测试套件 (ZATS) 返回 java.lang.NoClassDefFoundError : org/zkoss/zats/ZatsException

java - 在这种情况下如何声明 Java Comparable

c# - 如何获取具有指定名称的 DataMemberAttribute 的属性?

java - 如何使用反射检查方法是否是静态的?