问题:动态选择相应的子类并调用其方法,具体取决于传递的参数类型。
研究:努力将the generics approach应用于任务:
public abstract class MetaContainer
extends Node {
public static abstract interface CommonContainer {
ObservableList<Object> getChildren(Object container);
}
public abstract class AnchorPaneContainer
extends AnchorPane
implements CommonContainer {
public ObservableList<Object> getChildren(Object container) {
// Special approach for AnchorPanes.
}
}
public abstract class TabPaneContainer
extends TabPane
implements CommonContainer {
public ObservableList<Object> getChildren(Object container) {
// Special approach for TabPanes.
}
}
}
我尝试使用这样的类(并获取错误,因为CommonContainer是一个接口,不能有静态方法):
private ObservableList<Node> getElements(Node container)
throws ClassNotFoundException {
ObservableList<Node> nodes = FXCollections.observableArrayList();
ObservableList<Object> objects = FXCollections.observableArrayList();
objects.addAll(
MetaContainer.CommonContainer.
getChildren(
(Object) container));
for (Object object : objects) {
nodes.add(
(Node) object);
}
return nodes;
}
问题:如何在整个getChildren()
上调用MetaContainer
并在参数中传递任何类型的容器,等待它在容器类型的子类中寻址正确的getChildren()
? / p>
解释:简而言之,我需要浏览节点容器以搜索简单的控件。所以你不知道事先是什么类型的节点 - 只是在迭代时动态地。一些子节点也是必须浏览的容器,但每种类型都需要特定的方法。我可以在类型上做类似switch-case的事情,但觉得应该做一些更优雅的事情,比如每个类型的子类和一个通用接口。
答案 0 :(得分:1)
document.getElementById('yourObject').style.backgroundImage='url('+yourString+')';
子类的子节点来获得场景图的子集(即不一定只是通过调用Parent.getChildrenUnmodifiable()
)。因此,如果它是一个简单的Parent
,您只需拨打Pane
,但如果它是getChildren()
,您将获得每个TabPane
并获取每个标签的内容,从而形成从那些收集。 (同样对于其他“容器类型控件”,例如Tab
等)如果它是“简单”控件,则不要将其视为具有任何子节点(即使在幕后,例如,SplitPane
包含Button
。
所以我认为你也许可以通过构建一个类型安全的异构容器(参见Josh Bloch的Effective Java)来实现这一点,该容器将特定节点类型Text
映射到N
。该函数将定义如何检索该类型的子节点。
这可能看起来像
Function<N, List<Node>>
现在你可以调用public class ChildRetrievalMapping {
public static final ChildRetrievalMapping DEFAULT_INSTANCE = new ChildRetrievalMapping() ;
static {
// note the order of insertion is important: start with the more specific type
DEFAULT_INSTANCE.put(TabPane.class, tabPane ->
tabPane.getTabs().stream().map(Tab::getContent).collect(Collectors.toList()));
DEFAULT_INSTANCE.put(SplitPane.class, SplitPane::getItems);
// others...
// default behavior for "simple" controls, just return empty list:
DEFAULT_INSTANCE.put(Control.class, c -> Collections.emptyList());
// default behavior for non-control parents, return getChildrenUnmodifiable:
DEFAULT_INSTANCE.put(Parent.class, Parent::getChildrenUnmodifiable);
// and for plain old node, just return empty list:
DEFAULT_INSTANCE.put(Node.class, n -> Collections.emptyList());
}
private final Map<Class<?>, Function<? ,List<Node>>> map = new LinkedHashMap<>();
public <N extends Node> void put(Class<N> nodeType, Function<N, List<Node>> childRetrieval) {
map.put(nodeType, childRetrieval);
}
@SuppressWarnings("unchecked")
public <N extends Node> Function<N, List<Node>> getChildRetrieval(Class<N> nodeType) {
return (Function<N, List<Node>>) map.get(nodeType);
}
@SuppressWarnings("unchecked")
public List<Node> firstMatchingList(Node n) {
for (Class<?> type : map.keySet()) {
if (type.isInstance(n)) {
return getChildRetrieval((Class<Node>) type).apply(n);
}
}
return Collections.emptyList();
}
}
并获得地图中第一种类型定义的子节点列表,该节点与节点匹配。因此,使用childRetrievalMapping.findFirstMatchingList(node);
,如果您将DEFAULT_INSTANCE
传递给它,它将获得所有内容节点;如果你传递TabPane
,它就会得到物品;如果你传递了另一种类型的控件,它将返回一个空列表等等。
以下是使用此示例。这只是构建一个场景图,然后当你按下按钮时,它会遍历它,只获得上述类中策略定义的“简单”节点。 (然后它选择SplitPane
的所有实例并将Labeled
的结果传递给系统控制台。)注意它(故意)如何避免作为选项卡本身实现的一部分的标签,天真的root.lookupAll(".labeled")
不会做。
getText()
我不确定这是不是你想做什么,如果有的话,可能会有更优雅的方法来接近它。但我认为为特定类型声明这样的策略比对类型的大转换要好得多,并且可以选择将其配置为具有您想要的特定规则。