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) { } }
3 Answers
Answers 1
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.
Answers 2
the message
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)
says, that DEF
is declared as a list
of ColDef<BauwerkOption, ? extends Object>
and expects something, that extends an Object
and not an Object
itself. so if you remove ? extends
from the declaration of DEF
, it should work
Edit
to be more specific in your code, you could create Value-Object-Classes for Bauwerkstyp
, Bezeichnung
, Nutzungsart
, Durchmesser
, etc. that extend the Type you want, for example String
or Double
and implement an Interface
public ColDefStringValue implements IColDefValue<String> { @Override public String getValue() { return ...; } }
the interface could look like this
public interface ColDefValue<T> { T getValue(); }
the Interface could be used instead of ? extends Object
or Object
private static List<ColDef<BauwerkOption, IColDefValue<?>>> DEF = new ArrayList<>();
Answers 3
The problem is with ColDef#getValueSetterFunction()
. This returns a BiConsumer<BauwerkOption, ?>
, where the question mark refers to some type extending from Object
. There's no way to know which specific type this is but it is definitely not Object
itself. Passing a value of type Object
to the accept
method in TableModelSelektierung#setValueAt()
will thus fail with:
incompatible types: java.lang.Object cannot be converted to capture#1 of ?
0 comments:
Post a Comment