如何解决有没有办法将接口实例添加到 Java 中的 ArrayList?
我找到了 this
其中接受的答案几乎是完美的,但我想将元素添加到 ArrayList。这可能吗?
我会尽量简洁:我正在创建一个 JavaFX 应用程序,我有一个包含我所有 TextField 的 ArrayList。我使用这个列表为每个字段添加了一个 Action 事件的事件处理程序,这个处理程序调用了一个将焦点移动到列表中的下一个字段的方法(这样用户可以按回车键并自动导航到下一个字段) .
在同一个循环中,我还为每个字段添加了一个事件侦听器,以便在字段失去焦点时调用另一个方法来更新表单。通过这种方式,用户可以随意导航(返回、选项卡、鼠标单击等),并且表单将在他们完成输入数据后自动更新,无需单击按钮。
现在,在 update 方法中,我确定哪个字段触发了事件并验证该字段中的文本并从中解析一个双精度值。然后我需要对这些数据做一些事情。这是我想使用我的“工作函数”ArrayList 的地方。我可以简单地创建一个与字段遍历ArrayList的索引顺序匹配的并行ArrayList,并调用正确的更新方法。
我想这样做是因为每个 TextField 都绑定到特定对象的特定成员,例如 - TextField qtyOrdered
将绑定到 workOrder.qtyOrdered
成员,而 TextField {{ 1}} 将绑定到 materialWidth
成员。这些对象当然会有自己的访问器和修改器,所以我想使用接口预先创建所有这些方法,并根据 TextField 的索引调用正确的方法。
我想要这样的东西(伪代码):
masterRoll.materialWidth
ArrayList<Worker> workerMethods = new ArrayList<>();
workerMethods.add(new Worker() {
public void work(double input) {
workOrder.setQtyOrdered(input);
}
});
//Add remaining methods...
method updateForm(TextField caller)
{
get callerIndex;
// validate user input
if (valid)
workerMethods.get(callerIndex).work(input);
}
但是,当我使用相同的语法添加到 ArrayList 时,它显然不起作用。
如果可能,我想使用 ArrayList 而不是数组。这是因为表单中的某些字段在停用时会从 traversableFields ArrayList 中删除,以便字段遍历将在这些字段隐藏时跳过这些字段,并在它们可见时以正确的顺序恢复。例如,如果用户想要估计主卷上材料的长度,他们可以单击复选框以显示这些字段。
该 CheckBox 的 Action 事件处理程序然后将调用我的 updateControls() 方法,该方法将启用所需字段,将它们设置为可见,并将它们添加到可遍历字段列表中。如果他们取消选中它,则会发生相反的情况。
如果我使用数组来存储我的更新方法,则索引可能在任何给定时间都与我的 traversableFields 列表的索引不匹配。
编辑:在下面添加了我的实际代码(这是我在小型测试环境中使用的简化版本)。
用于创建可遍历文本字段列表的代码:
workerMethods[callerIndex].update(callerValue);
// ArrayList of traversable TextFields for form navigation.
private ArrayList<TextField> traversableFields = new ArrayList<>();
// Add all TextFields to the ArrayList.
traversableFields.addAll(Arrays.asList(field1,field2,field3));
// Loop through the ArrayList to add handlers/listeners.
for (int i = 0; i < traversableFields.size(); i++) {
// Get the Control at index i in the ArrayList.
TextField currentControl = traversableFields.get(i);
// Add an event handler so that when the user presses return we can
// move to the next field in the ArrayList.
currentControl.addEventHandler(ActionEvent.ACTION,e ->
movetoNextField(e));
// Add a listener so that if a field loses focus we update the form.
currentControl.focusedproperty().
addListener((obs,wasFocused,isNowFocused) ->
{
if (!isNowFocused) {
updateForm(currentControl);
}
});
}
上面的片段基本上就是他们在我发布的链接中所做的。正如我之前提到的,这是有效的。我可以调用 interface Update { public void update(double input); }
class Label1 { public void update(double input) { label1.setText(String.valueOf(input)); } }
class Label2 { public void update(double input) { label2.setText(String.valueOf(input)); } }
class Label3 { public void update(double input) { label3.setText(String.valueOf(input)); } }
Label1 l1 = new Label1();
Label2 l2 = new Label2();
Label3 l3 = new Label3();
Update[] updateFunctions = new Update[] {
new Update() { public void update(double input) { l1.update(input); } },new Update() { public void update(double input) { l2.update(input); } },new Update() { public void update(double input) { l3.update(input); } }
};
并且它做我想做的事。这是事件处理程序调用的方法:
updateFunctions[callerIndex].update(callerValue);
// Move to the next field in the form. Called by the Action Event for each
// field in the traversableFields ArrayList.
private void movetoNextField(ActionEvent event)
{
// Get the TextField that triggered the event.
TextField caller = (TextField)event.getSource();
// Get the index of this Control from the ArrayList.
int callerIndex = traversableFields.indexOf(caller);
// If we have reached the end of the list then we move to the
// first field in the list.
if (callerIndex == traversableFields.size() - 1)
traversableFields.get(0).requestFocus();
// Otherwise move to the next field.
else
traversableFields.get(++callerIndex).requestFocus();
}
但是,我想使用 ArrayList 而不是数组。但如果我这样做:
// Update the form. This will contain the majority of functional code
// and will be called automatically when any TextField loses focus.
private void updateForm(TextField caller)
{
// Get the index of the TextField
int callerIndex = traversableFields.indexOf(caller);
// Get the CSS id of the TextField for testing purposes.
String callerID = caller.getId();
// Get the TextField contents.
String callerText = caller.getText();
// Flag variable.
boolean fieldEmpty = callerText.equals("");
// Double for storing parsed input.
double callerValue = 0;
// If the field is not empty,ensure that it contains valid data before
// proceeding.
if (!fieldEmpty)
{
try
{
callerValue = Validation.getDouble(callerText);
updateFunctions[callerIndex].update(callerValue);
clearError(caller);
}
catch (Exception e)
{
// If the input was invalid,alert the user and move focus
// back to the offending TextField.
System.out.println("Invalid input.");
markEntryInvalid(caller);
caller.requestFocus();
}
}
// Trace statements.
System.out.println("updateForm() called by " + callerID);
System.out.println("Parsed value was " + callerValue);
}
我收到 ArrayList<Update> updateMethods = new ArrayList<>();
updateMethods.add(new Update() { public void update(double input) { l1.update(input); } });
错误。
解决方法
如果我正确理解您的问题,您想知道的是如何使用给定的值集合初始化 ArrayList
?
应该是这样的:
List<Workers> myWorkers = new ArrayList<>(
Arrays.asList(
new Worker() { ... },new Worker() { ... }
)
);
Arrays.asList(element1,element2,...)
返回这些元素的不可变列表。而 ArrayList
的构造函数可以使用 Collection
来初始化列表。
当然,您也可以创建列表并添加单个元素。结果列表是一样的:
List<Workers> myWorkers = new ArrayList<>();
myWorkers.add(new Worker() { ... });
myWorkers.add(new Worker() { ... });
在这两种情况下,都可以使用 add
和 remove
方法修改结果列表。
关于用例的两个注意事项:
首先,Map
可能更适合您的用例,因为您不需要关心索引:
// initialization:
Map<TextField,Worker> workers = new HashMap<TextField,Worker>();
workers.put(textfield1,worker1);
workers.put(textfield2,worker2);
// ...
void updateForm(TextField caller) {
workers.get(caller).work();
}
第二,如果你的 Worker 接口只有一个方法,从 Java 8 开始你可以将它用作带有闭包的函数式接口。所以初始化看起来像这样:
Map<TextField,input -> { /* do something with input here */ });
workers.put(textfield2,input -> { /* do something with input here */ });
也许甚至不需要您的 Worker
接口,您可以使用 Consumer<InputType>
代替。
您可以创建List<Runnable>
并在当前线程或并行线程中执行代码:
List<Runnable> workerMethods = List.of(
() -> System.out.println("worker 1"),() -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get(0).run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get(1)).start(); // worker 2
同样,您可以创建 Map<String,Runnable>
:
Map<String,Runnable> workerMethods = Map.of(
"w1",() -> System.out.println("worker 1"),"w2",() -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get("w1").run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get("w2")).start(); // worker 2
,
我可以建议一种完全不同的方法吗?
为什么不使用 TextField 的用户数据字段来指向您自己的可以满足您所有需求的对象。
例如
class FieldContext {
TextField previous;
TextField next;
Update updater;
// add whatever else you need,e.g. validation interface etc.
}
因此,当您从 TextField 获取事件时,您只需调用:
FieldContext fieldCtx = (FieldContext)((TextField)event.getSource()).getUserData();
然后您可以自由地处理任何需要的具有 TexTField 上下文信息的特定事件处理。
我发现很多人都忽略了一个事实,即您在控件上有一个用户数据字段。我发现它可以简化很多事情
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。