Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make BiConsumer accept object

Tags:

java

I have a list of column definitions for a JTable TableModel with the column "B" having a setter BiConsumer which accepts a BauwerkOption class and a string.

When I try to set the string in the line with "...accept..." I get following error:

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)

What is wrong with my code? Is it even possible what I am trying to do?

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) {

    }
}
like image 704
ave4496 Avatar asked Apr 11 '18 06:04

ave4496


1 Answers

There is no clean way of doing this, since the type of the Object parameter in setValueAt cannot be changed.

If you replace the following code, it will be as clean as you can get:

This is just for convenience (you don't need own lambdas):

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

In ColDef simply add this method without changing anything else:

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

And then replace setValueAt in TableModelSelektierung with:

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

Keeps your generics in place but works around the Object restriction you get from inheriting. You should probably add type checks in setValueAt to make sure the object parameter is of the correct type.

like image 89
Sebastian Avatar answered Oct 16 '22 17:10

Sebastian