2016-03-07 3 views
2

Чтобы облегчить создание TableView со столбцами в приложении JavaFX, я подумал о создании аннотации для указания поля в класс модели, который я хотел бы создать TableColumn со значением типа ячейки, связанным с ним. Вот аннотацию я писал:JavaFx Использование отражения с пользовательской аннотацией для создания 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 
} 

В родном пути создания TableView с колоннами в JavaFX, я должен писать код, как это :

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

Дженерики просто обеспечивают безопасность во время компиляции и не работают во время выполнения. –

+0

Я бы указал тип поля, используя тип класса –

+0

@SvenOlderaan. Но проблема заключается в создании экземпляра универсального класса во время выполнения, не получая его общий тип генерации – qingl97

ответ

3

Вы можете создать метод

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);, но я думаю, вам придется делать специальные случаи для примитивных типов.

+0

А, это решение. Но что делать, если у меня нет полей свойств в классе модели, так что я намерен создать фабричное значение ячейки как (не настоящий код) 'col.setCellValueFactory (cellData -> new SimpleObjectProperty (cellData.getValue()).getFieldValue())); 'Теперь я не могу понять, как создать экземпляр SimpleObjectProperty с параметром type с значением поля. ; (У вас есть предложения? – qingl97

+0

См. Обновления для ответа. –

+0

, который отлично работает! Спасибо за ответ rapide – qingl97

Смежные вопросы