致电子课程'方法取决于Java中的对象类型

时间:2016-01-04 18:41:11

标签: java generics types javafx casting

问题:动态选择相应的子类并调用其方法,具体取决于传递的参数类型。

研究:努力将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的事情,但觉得应该做一些更优雅的事情,比如每个类型的子类和一个通用接口。

1 个答案:

答案 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()

我不确定这是不是你想做什么,如果有的话,可能会有更优雅的方法来接近它。但我认为为特定类型声明这样的策略比对类型的大转换要好得多,并且可以选择将其配置为具有您想要的特定规则。