使用带有通配符的Java泛型时的“类型不匹配”

使用带有通配符的Java泛型时的“类型不匹配”,java,generics,Java,Generics,我在Java中使用泛型时遇到了一个奇怪的问题。泛型对我来说是很新的,但我想我了解基本知识 请看一下这段代码: private void drawQuadtreeBoxes_helper(QuadTree<?> node) { if (node == null) return; Vector3 min = node.bbox.min; Vector3 max = node.bbox.max; // Draw the boxes (...

我在Java中使用泛型时遇到了一个奇怪的问题。泛型对我来说是很新的,但我想我了解基本知识

请看一下这段代码:

private void drawQuadtreeBoxes_helper(QuadTree<?> node) {
    if (node == null)
        return;

    Vector3 min = node.bbox.min;
    Vector3 max = node.bbox.max;
    // Draw the boxes (...)

    if (node.hasChildren()) {
        Array<QuadTree<?>> children = node.getChildren(); // ERROR HERE
        for (QuadTree<?> child : children) {
            drawQuadtreeBoxes_helper(child);
        }
    }
}
由于四叉树结构中存储的对象类型与此方法无关,因此我使用通配符作为方法签名,因此此方法可以应用于所有类型的四叉树


方法getChildren返回节点的四个子节点,这些子节点存储在名为Array的集合类中。我确信getChildren的返回类型确实是Array问题是该方法不知道它是同一个四叉树?可以在同一调用中引用不同的类型

解决方案是键入方法,该方法锁定在四叉树中,因此?在整个方法中使用相同的类型

private <T extends QuadTree<?>> void drawQuadtreeBoxes_helper(T node) {
    if (node == null)
        return;

    Vector3 min = node.bbox.min;
    Vector3 max = node.bbox.max;
    // Draw the boxes (...)

    if (node.hasChildren()) {
        Array<T> children = node.getChildren(); // ERROR HERE
        for (T child : children) {
            drawQuadtreeBoxes_helper(child);
        }
    }
}

? 仍然意味着什么,但它现在是相同的任何东西。

问题是方法不知道它是同一个四叉树?可以在同一调用中引用不同的类型

解决方案是键入方法,该方法锁定在四叉树中,因此?在整个方法中使用相同的类型

private <T extends QuadTree<?>> void drawQuadtreeBoxes_helper(T node) {
    if (node == null)
        return;

    Vector3 min = node.bbox.min;
    Vector3 max = node.bbox.max;
    // Draw the boxes (...)

    if (node.hasChildren()) {
        Array<T> children = node.getChildren(); // ERROR HERE
        for (T child : children) {
            drawQuadtreeBoxes_helper(child);
        }
    }
}

? 仍然意味着任何东西,但现在它仍然是相同的任何东西。

如果您不关心四叉树中的类型参数,请简单地从代码中删除所有内容:只需尝试以下代码,编译器就会认为它是正确的:

private void drawQuadtreeBoxes_helper(QuadTree node) {
    if (node == null)
        return;

    Vector3 min = node.bbox.min;
    Vector3 max = node.bbox.max;
    // Draw the boxes (...)

    if (node.hasChildren()) {
        Array<QuadTree> children = node.getChildren();
        for (QuadTree child : children) {
            drawQuadtreeBoxes_helper(child);
        }
    }
}

若您不关心四叉树中的类型参数,请从代码中简单地到处删除:只需尝试以下代码,编译器就会认为它正常:

private void drawQuadtreeBoxes_helper(QuadTree node) {
    if (node == null)
        return;

    Vector3 min = node.bbox.min;
    Vector3 max = node.bbox.max;
    // Draw the boxes (...)

    if (node.hasChildren()) {
        Array<QuadTree> children = node.getChildren();
        for (QuadTree child : children) {
            drawQuadtreeBoxes_helper(child);
        }
    }
}

除了Bohemian的回答之外,我想指出,您仍然可以拥有相同的签名,您希望没有人需要知道您正在使用这个T类型参数,因为这是一个实现细节

您可以通过创建一个具有原始类型签名的包装器方法来实现这一点,该方法使用T调用泛型方法。该调用由于捕获而起作用

private void drawQuadtreeBoxes_helper(QuadTree<?> node) {
    drawQuadtreeBoxes_helper_private(node);
}

private <T extends QuadTree<?>> void drawQuadtreeBoxes_helper_private(T node) {
    // code here ...
}

当然,因为示例中的方法是私有的,所以您可能不必费心去做所有这些。但是如果它是一个公共API,那么这样做可能是一个好主意,将T的不必要的实现细节抽象出来。

除了Bohemian的答案之外,我想指出的是,您仍然可以拥有相同的签名,您希望没有人需要知道您正在使用这个T类型参数,因为这是一个实现细节

您可以通过创建一个具有原始类型签名的包装器方法来实现这一点,该方法使用T调用泛型方法。该调用由于捕获而起作用

private void drawQuadtreeBoxes_helper(QuadTree<?> node) {
    drawQuadtreeBoxes_helper_private(node);
}

private <T extends QuadTree<?>> void drawQuadtreeBoxes_helper_private(T node) {
    // code here ...
}

当然,因为示例中的方法是私有的,所以您可能不必费心去做所有这些。但是如果它是一个公共API,那么这样做可能是一个好主意,将T的不必要的实现细节抽象掉。

非常有意义!非常感谢。这是一个非常方便记住的模式。每次使用泛型时,你的信心和理解力都会提高:顺便说一句,我更喜欢另一个人刚刚发布的风格,但他删除了他的答案,因为他比你慢了大约3秒。他建议使用这个方法签名:private void drawQuadtreeBoxes\u helperQuadTree node,但你们都完全正确。再次感谢!顺便说一句,这是我在这个网站上的第一个问题,它在几秒钟内就被回答了!这就像魔术一样,我为你们感到惊讶。这取决于你们想做什么。这两种样式都适用于您,但my保留了四叉树的可能性,调用方可能需要四叉树,但问题中没有说明这一点,这是非常有意义的!非常感谢。这是一个非常方便记住的模式。每次使用泛型时,你的信心和理解力都会提高:顺便说一句,我更喜欢另一个人刚刚发布的风格,但他删除了他的答案,因为他比你慢了大约3秒。他建议使用这个方法签名:private void drawQuadtreeBoxes\u helperQuadTree node,但你们都完全正确。再次感谢!顺便说一句,这是我在这个网站上的第一个问题,它在几秒钟内就被回答了!这就像魔术一样,我为你们感到惊讶。这取决于你们想做什么。这两种样式都适用于您,但my保留了四叉树的可能性,这可能是调用者所需要的,在本例中,Eclipse警告不要使用原始类型。我不得不使用@SuppressWarning注释来消除警告,但我不想这样做。另外,我在某个地方读到,Java可能会在特性中的某个时候禁止使用原始类型。通常我不会在意,但这段代码是我硕士论文的一部分,会被审查,所以我想保持干净的风格。无论如何谢谢你!在这种情况下,Eclipse警告不要使用原始类型。我不得不使用@SuppressWarning注释来消除警告,但我不想这样做。另外,我在某个地方读到,Java可能会在特性中的某个时候禁止使用原始类型。通常我不会在意,但这段代码是我硕士论文的一部分,会被审查,所以我想保持干净的风格。无论如何谢谢你!