JavaFx使用带有自定义注释的反射来创建TableColumn:can';t创建参数类型类的实例

JavaFx使用带有自定义注释的反射来创建TableColumn:can';t创建参数类型类的实例,java,generics,reflection,javafx,annotations,Java,Generics,Reflection,Javafx,Annotations,为了便于在JavaFX应用程序中创建带有列的TableView,我考虑创建一个注释来指示模型类中的字段,我想创建一个单元格值类型与之关联的TableColumn。这是我写的注释: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface AsTableColumn { String text(); int index(); } 还有一个示例模型类人员,如下所示: publi

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

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

    String text();
    int index();
}
还有一个示例模型类人员,如下所示:

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

    // setters and getters
}
按照在JavaFX中创建带有列的TableView的本机方式,我必须编写如下代码:

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()));
TableView table=newtableview();
TableColumn Name col=新的TableColumn(“名称”);
TableColumn ageCol=新的TableColumn(“年龄”);
nameCol.setCellValueFactory(cellData->new SimpleObject属性(cellData.getValue().getName());
ageCol.setCellValueFactory(cellData->new SimpleObject属性(cellData.getValue().getAge());
我有几个表有很多列,所以写很多这样的代码很烦人,我认为应该有一种方法来简化它

在编写注释处理器时,我遇到了一个关于创建
TableColumn
实例的问题,其中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<>();
            }
        }
    }
}
import java.lang.reflect.Field;
导入javafx.scene.control.TableColumn;
公共类注释处理器{
/**
*处理模型类。
* 
*对于声明的具有注释AsTableColumn的字段,创建TableColumn
*具有检测到的字段类型的实例。
*@param-clas
*/
公共静态列表进程(clas类){
for(字段:clas.getDeclaredFields()){
if(field.isAnnotationPresent(AsTableColumn.class)){
AsTableColumn anno=field.getAnnotation(AsTableColumn.class);
类类型=field.getType();
TableColumn col=新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 ;
}

如果您不使用JavaFX属性,那么很容易将其调整为只生成
私有静态TableColumn列(字符串文本、函数属性),但我认为您必须为基元类型设置特殊情况。

泛型仅提供编译时安全性,在运行时不存在。我将使用类指定字段类型type@SvenOlderaan但问题是在运行时创建泛型类的实例,无法获取其运行时泛型类型泛型中唯一可以使用的类型是“Object”,因为您不知道字段是否为字符串、int、long等。注释处理器(请参阅)是在编译时运行的。您在这里所做的只是使用反射访问注释。啊,这是一个解决方案。但是,如果我在模型类中没有属性字段,因此我打算创建单元格值工厂,比如(不是真正的代码)
col.setCellValueFactory(cellData->new SimpleObjectProperty(cellData.getValue().getFieldValue())现在我不知道如何使用字段值创建类型参数化实例SimpleObjectProperty;(你有什么建议吗?
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);
    }

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