Сделать объект 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 ?