Сделать объект BiConsumer приемлемым

У меня есть список определений столбцов для JTable TableModel с столбцом "B", имеющим набор BiConsumer, который принимает класс BauwerkOption и строку.

Когда я пытаюсь установить строку в строке с помощью "... accept...", я получаю следующую ошибку:

The method accept(Selektierung.BauwerkOption, capture#4-of ? extends Object) in the type BiConsumer<Selektierung.BauwerkOption,capture#4-of ? extends Object> is not applicable for the arguments (Selektierung.BauwerkOption, Object)

Что не так с моим кодом? Возможно ли, что я пытаюсь сделать?

public class TableModelSelektierung extends DefaultTableModel {
    private static final long serialVersionUID = -5921626198599251183L;
    private List<BauwerkOption> data;
    private static List<ColDef<BauwerkOption, ? extends Object>> DEF = new ArrayList<>();
    static {
        DEF.add(new ColDef<BauwerkOption, String>("A", (o) -> o.getBauwerkstyp()));
        DEF.add(new ColDef<BauwerkOption, String>("B", (o) -> o.getBezeichnung())
                                                                                            .withSetValueAtFunction((i, o) -> i.setBauwerkstyp(o)));
        DEF.add(new ColDef<BauwerkOption, String>("C", (o) -> o.getNutzungsart()));
        DEF.add(new ColDef<BauwerkOption, Double>("D", (o) -> o.getDurchmesser()));
    }

    @Override
    public int getColumnCount() {
        return DEF.size();
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return DEF.get(row).getValueSetterFunction() == null;
    }

    @Override
    public int getRowCount() {
        if (data != null) {
            return data.size();
        }
        return 0;
    }

    @Override
    public String getColumnName(int column) {
        return DEF.get(column).getTitle();
    }

    public void setData(List<BauwerkOption> data) {
        this.data = data;
        fireTableDataChanged();
    }

    public BauwerkOption getObjectAt(int row) {
        return data.get(row);
    }

    @Override
    public void setValueAt(Object aValue, int row, int column) {
        if (DEF.get(column).getValueSetterFunction() != null) {
            DEF.get(column).getValueSetterFunction().accept(getObjectAt(row), aValue);
        }
    }

    @Override
    public Object getValueAt(int row, int column) {
        BauwerkOption o = getObjectAt(row);
        return DEF.get(column).getValueGetterFunction().apply(o);
    }

}


class ColDef<InputObjekt, OutputValue> {
    private String title;
    private Function<InputObjekt, OutputValue> valueGetterFunction;
    private BiConsumer<InputObjekt, OutputValue> valueSetterFunction;

    public ColDef(String title, Function<InputObjekt, OutputValue> valueGetterFunction) {
        this.title = title;
        this.valueGetterFunction = valueGetterFunction;
    }

    public ColDef<InputObjekt, OutputValue> withSetValueAtFunction(BiConsumer<InputObjekt, OutputValue> valueSetterFunction) {
        this.valueSetterFunction = valueSetterFunction;
        return this;
    }

    public Function<InputObjekt, OutputValue> getValueGetterFunction() {
        return valueGetterFunction;
    }

    public String getTitle() {
        return title;
    }

    public BiConsumer<InputObjekt, OutputValue> getValueSetterFunction() {
        return valueSetterFunction;
    }

}

class BauwerkOption {


    public BauwerkOption() {

    }

    public String getBezeichnung() {
        return "";
    }

    public String getBauwerkstyp() {
        return "";
    }

    public Double getDurchmesser() {

        return 0d;
    }

    public String getNutzungsart() {
        return "todo";
    }



    public void setBauwerkstyp(String s) {

    }
}

Ответы

Ответ 1

Нет чистого способа сделать это, так как тип параметра Object в setValueAt не может быть изменен.

Если вы замените следующий код, он будет таким же чистым, как вы можете:

Это просто для удобства (вам не нужны собственные лямбды):

private static List<ColDef<BauwerkOption, ?>> DEF = new ArrayList<>();
static {
    DEF.add(new ColDef<BauwerkOption, String>("A", BauwerkOption::getBauwerkstyp));
    DEF.add(new ColDef<BauwerkOption, String>("B", BauwerkOption::getBezeichnung).withSetValueAtFunction(BauwerkOption::setBauwerkstyp));
    DEF.add(new ColDef<BauwerkOption, String>("C", BauwerkOption::getNutzungsart));
    DEF.add(new ColDef<BauwerkOption, Double>("D", BauwerkOption::getDurchmesser));
}

В ColDef просто добавьте этот метод, не изменяя ничего:

@SuppressWarnings("unchecked")
public BiConsumer<InputObjekt, Object> getObjectValueSetterFunction() {
    return (BiConsumer<InputObjekt, Object>) valueSetterFunction;
}

А затем замените setValueAt в TableModelSelektierung на:

@Override
public void setValueAt(Object aValue, int row, int column) {
    if (DEF.get(column).getValueSetterFunction() != null) {
        DEF.get(column).getObjectValueSetterFunction().accept(getObjectAt(row), aValue);
    }
}

Сохраняет ваши дженерики на месте, но работает вокруг ограничения объекта, которое вы получаете от наследования. Вероятно, вы должны добавить проверки типов в setValueAt, чтобы убедиться, что параметр объекта имеет правильный тип.

Ответ 2

сообщение

The method accept(Selektierung.BauwerkOption, capture#4-of ? extends Object) in the type BiConsumer<Selektierung.BauwerkOption,capture#4-of ? extends Object> is not applicable for the arguments (Selektierung.BauwerkOption, Object)

говорит, что DEF объявлен как list ColDef<BauwerkOption,? extends Object> ColDef<BauwerkOption,? extends Object> и ожидает чего-то, что расширяет Object а не сам Object. так что если вы удалите ? extends ? extends от декларации DEF, она должна работать

редактировать

чтобы быть более точным в вашем коде, вы можете создать Value-Object-классы для Bauwerkstyp, Bezeichnung, Nutzungsart, Durchmesser и т.д., которые расширяют тип вы хотите, например, String или Double и реализовать интерфейс

public ColDefStringValue implements IColDefValue<String> {
    @Override
    public String getValue() {
        return ...;
    }
}

интерфейс может выглядеть так:

public interface ColDefValue<T> {
    T getValue();
}

интерфейс можно использовать вместо ? extends Object ? extends Object или Object

private static List<ColDef<BauwerkOption, IColDefValue<?>>> DEF = new ArrayList<>();

Ответ 3

Проблема заключается в ColDef#getValueSetterFunction(). Это возвращает BiConsumer<BauwerkOption,?>, Где вопросительный знак относится к некоторому типу, простирающемуся от Object. Там нет способа узнать, какой именно тип это, но это определенно не сам Object. Таким образом, передача значения типа Object в метод accept в TableModelSelektierung#setValueAt() завершится неудачей:

incompatible types: java.lang.Object cannot be converted to capture#1 of ?