lundi 7 mars 2016

Create instance of generic class whose type parameter is dynamically detected by the refered field

In order to facilitate the creation of a TableView with columns in a JavaFX application, I thought of creating an annotation to indicate the field in a model class that I would like to create a TableColumn with cell value type associated with it. Here is the annotation I wrote:

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

    String text();
    int index();
}

And a sample model class Person like this :

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

    // setters and getters
}

In the native way of creating a TableView with columns in JavaFX, I have to write code like this:

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

I have several tables with a lot of columns, so it's annoying to write a lot of code like this which I think there should be a way to simplify it.

When I wrote the annotation processor, I encountered a problem about creating a TableColumn instance where the T is parametered in the method signature but S is dynamically detected at runtime.

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





Aucun commentaire:

Enregistrer un commentaire