Как создать универсальный метод для преобразования List <Object>в ObservableList <S>и Map? - PullRequest
0 голосов
/ 25 августа 2018

Относительный вопрос новичка в Java.

У меня есть код, который создает наблюдаемый список ObservableList<TableModel> и хэш-карту Map<Integer, TableModel> из двумерного списка List<List<Object>>.

Я хотел бы преобразовать код в универсальный метод, который будет работать с любой табличной моделью.

Однако я застрял в знании того, как заменить конкретные типы параметрами универсального типа в двух местах в моем коде.Может ли кто-нибудь помочь мне, пожалуйста?

Мой текущий неуниверсальный код выглядит следующим образом:

//A database table is loaded into resultSet
private static List<List<Object>> resultSet = new ArrayList<>();

//...

ObservableList<TableModel> olModel = FXCollections.observableArrayList();
Map<Integer, TableModel> mapModel = new HashMap<>();

resultSet.forEach(resultRow -> {

    //Load the observable list
    TableModel row = new TableModel();
    row.addList(resultRow);
    olModel.add(row);

    //Load the hash map
    mapModel.put(row.getPK(), row);

});

Я пытаюсь «обобщить» его следующим образом и указал места, где яЯ застрял:

//Call a generic method
loadTableOLAndHM(olModel, mapModel, resultSet);

//...

private <S, T> void loadTableOLAndHM(ObservableList<S> ol, Map<T, S> hm, List<List<Object>> rs) {

    rs.forEach(resultRow -> {

        S row = new [WHAT-GOES-HERE?] (); //(1) STUCK HERE
        row.addList(resultRow);
        ol.add( (S) row);

        hm.put( (T) row.getPK(), (S) row); //(2) STUCK HERE - GETS "incompatible types" ERROR

    });

}

Я могу обойти ошибку "несовместимых типов" в (2), изменив getPK(), чтобы он возвращал Integer вместо int, но не знаю, так ли этоправильный способ исправить это.Первичным ключом БД будут и другие типы данных для других таблиц, поэтому я не уверен, что делать.

Я не знаю, как исправить (1).

IЯ читал и экспериментировал с различными способами составления списка результатов, но, похоже, ни один из них не работает.Еще я попробовал передать TableModel.class в качестве параметра универсальному методу, но не смог понять, как его использовать.

Вот MVCE, который показывает, что я сейчас делаю, и какну, что я хотел бы сделать.Если у вас есть время помочь, я был бы очень признателен.Спасибо!

Я использую JavaFX8, NetBeans 8.2 и Scene Builder 8.3.

package test11;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Test11 extends Application {

    private static List<List<Object>> resultSet = new ArrayList<>();

    private Parent createContent() {

        BorderPane content = new BorderPane();

        //Load dummy data into resultSet for testing
        loadDummyData();

        //***********************************************************************
        //WHAT I'M CURRENTLY DOING

        //Observable list and hash map for the loaded table
        ObservableList<TableModel> olModel = FXCollections.observableArrayList();
        Map<Integer, TableModel> mapModel = new HashMap<>();

        resultSet.forEach(resultRow -> {

            //Load the observable list
            TableModel row = new TableModel();
            row.addList(resultRow);
            olModel.add(row);

            //Load the hash map
            mapModel.put(row.getPK(), row);

        });

        systemOutIt("***** CURRENT WAY ******", olModel, mapModel);

        olModel.clear();
        mapModel.clear();

        //***********************************************************************
        //WHAT I WOULD LIKE TO BE ABLE TO DO

        loadTableOLAndHM(olModel, mapModel, resultSet);

        systemOutIt("***** NEW WAY ******", olModel, mapModel);

        return content;

    }

    private <S, T> void loadTableOLAndHM(ObservableList<S> ol, Map<T, S> hm, List<List<Object>> rs) {

        rs.forEach(resultRow -> {

//==>STUCK GENERICISING THE FOLLOWING LINE:
//original line            TableModel row = new TableModel();

//            S row = new [WHAT-GOES-HERE?] ();

//            row.addList(resultRow);
//            ol.add( (S) row);

//==>STUCK GENERICISING THE FOLLOWING LINE
//Error "incompatible types:  int cannot be converted to T"
//In the Map declaration, T is Integer.
//In TableModel, getPK() returns int.

//            hm.put( (T) row.getPK(), (S) row);

        });

    }

    public static class TableModel {

        private final SimpleIntegerProperty PK;
        private final SimpleStringProperty dataField;

        private TableModel() {
            this(0, "");
        }

        private TableModel(int PK, String dataField) {
            this.PK = new SimpleIntegerProperty(PK);
            this.dataField = new SimpleStringProperty(dataField);
        }

        public int getPK() {
            return PK.get();
        }

        public void setPK(int PK) {
            this.PK.set(PK);
        }

        public IntegerProperty PKProperty() {
            return PK; 
        }

        public String getDataField() {
            return dataField.get();
        }

        public void setDataField(String dataField) {
            this.dataField.set(dataField);
        }

        public StringProperty dataFieldProperty() {
            return dataField; 
        }

        public void addList(List<Object> list) {

            this.PK.set( (int) list.get(0));
            this.dataField.set( (String) list.get(1));

        }

    }

    private void loadDummyData(){

        for ( int i=1; i<4; i++ ) {
            List<Object> resultSetRow = new ArrayList<>();
            resultSetRow.add(i);
            resultSetRow.add("string"+i);
            resultSet.add(resultSetRow);
        }

    }

    private void systemOutIt(String whichWay, ObservableList<TableModel> ol, Map<Integer, TableModel> hm) {

        System.out.println(whichWay);
        System.out.println("ObservableList, size is " + ol.size());
        for ( int i=0; i<ol.size(); i++ ) {
            System.out.println("     ol fields = " + ol.get(i).getPK() + ", " + ol.get(i).getDataField());
            System.out.println("     map dataField value = " + hm.get(ol.get(i).getPK()).getDataField());
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.setHeight(250);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

1 Ответ

0 голосов
/ 25 августа 2018

Давайте проанализируем необходимую вам функциональность:

  1. Создайте объект с List<Object> (это можно разделить на 2 части: создание объекта и инициализация объектов).
  2. Получить свойство id из объекта.

По крайней мере, для создания объекта вы должны использовать объект, обеспечивающий реализацию.Если вы хотите объединить это с инициализацией, вы можете использовать Function<List<Object>, S> для этого, если вы хотите отделить это, используйте Supplier<S>.

Для инициализации и / или получения идентификатора вы можете использовать методы S, который требует от вас ограничения его типа общим базовым классом или интерфейсами, содержащими методы для этого.В качестве альтернативы вы могли бы также использовать объекты, содержащие реализации, например BiConsumer<S, List<Object>> и Function<S, IdClass>.

Пример 1. Общий интерфейс, реализующий идентификатор / инициализацию

public interface DatabaseObject<T> {
    void addList(List<Object> list);

    T getId();
}
private static <K, E extends DatabaseObject<? extends K>> void loadTable(ObservableList<E> ol, Map<K, E> hm, List<List<Object>> rs, Supplier<E> factory) {
    for (List<Object> resultRow : rs) {
        E o = factory.get();
        o.addList(resultRow);
        hm.put(o.getId(), o);
        ol.add(o);
    }
}

Если выреализовать DatabaseObject<Integer> с TableModel, вы можете использовать метод, подобный этому:

loadTable(olModel, mapModel, resultSet, TableModel::new);

Пример 2. Использование различных объектов, содержащих реализации

private static <S, T> void loadTable(
        ObservableList<S> ol,
        Map<T, S> hm,
        List<List<Object>> rs,
        Supplier<? extends S> factory,
        BiConsumer<? super S, List<Object>> initializer,
        Function<? super S, ? extends T> idExtractor) {

    for (List<Object> resultRow : rs) {
        S o = factory.get();
        initializer.accept(o, resultRow);
        hm.put(idExtractor.apply(o), o);
        ol.add(o);
    }
}
loadTable(olModel, mapModel, resultSet, TableModel::new, TableModel::addList, TableModel::getPK);

Примечание: Будьте осторожны при хранении объектов на основе изменяемых свойств в Map.Вызов setPK для объекта, который был сохранен на карте, может легко сломать ваш код ...

...