为什么未经检查的警告不会被抑制

Why unchecked warning would not be suppressed

我有几个未经检查的警告,我想禁止显示,但在 运行 我仍然看到这条消息:

Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

我正在使用以下类型的注释,实际上 IDE 源代码中的警告消息消失了。

@Override @SuppressWarnings("unchecked") 
public GroundBase<J> getGround() {
    return ground;
}

我将我的代码作为 public API 分发,我想知道用户在使用过程中是否也会看到该消息。

我正在用 junit 测试我的代码。 我正在使用 Java 8 和 intelliJ 2016.3.6.

我已经查看了 -Xlint:unchecked 建议的详细信息。代码中不再有未注释的部分,但编译器建议仍然没有消失(或减少)。

编辑

为了更好地理解我收到的警告之一,这里是简化但仍然相关的部分代码:

abstract public class MORBase<J extends OWLObject>
    implements Descriptor<OWLReferences,J>, MORGround<J>

    @Override @SuppressWarnings("unchecked")
    public GroundBase<J> getGround() {
        return ground;
    }
}

interface Ground<O,J>{
    Ground<J> getGround();
}

interface Descriptor<O,J> {
    <I extends Ground<O,J>> I getGround();
}

这里是关于它的完整消息:

warning: [unchecked] getGround() in MORBase implements <I>getGround() in Descriptor
public GroundBase<J> getGround() {
                     ^
return type requires unchecked conversion from GroundBase<J#1> to I
where J#1,I,O,J#2 are type-variables:
    J#1 extends OWLObject declared in class MORBase
    I extends Ground<O,J#2> declared in method <I>getGround()
    O extends Object declared in interface Descriptor
    J#2 extends Object declared in interface Descriptor

我很欣赏关于界面设计的建议,但我的问题是关于为什么警告没有被抑制。

你的问题其实出在DescriptorgetGround()的定义上。类型变量 IgetGround() 的声明中是自由的,这意味着您的方法承诺 return 调用者 的任何类型]选择! return 类型 I 的值的唯一方法是以某种方式颠覆类型系统(例如,通过抛出异常或 returning null)。

编译器正确检测到 调用者 中的 IgetGround() 的使用可能与 J 在实现中的类型不同MORBase 中的 getGround(),但编译器无法发出任何字节码来检查类型(因为您正在编译 MORBase,但需要将字节码插入 来电者 getGround())。由于它无法检查类型,也无法插入代码来检查类型,因此它会正确发出未经检查的警告。

通过将 @SuppressWarnings 注释附加到 Descriptor 接口中 getGround() 的声明中,实际上可能会抑制此警告,但您确实不应该这样做。相反,请修复您的代码。

我推荐的修复方法是简单地在 getGround() 的声明中删除类型变量 I,并依靠子类型多态性来允许您简单地将 getGround() 声明为 returning Ground<O, J>:

interface Descriptor<O,J> {
    Ground<O,J> getGround();
}

如果那是不可能的,并且你需要能够在Descriptor的子类型中return Ground的子类型,那么你需要向[=14]添加一个类型参数=] 以确保类型正确传播给调用者:

interface Descriptor<O, J, G extends Ground<O, J>> {
    G getGround();
}

请注意,即使 Descriptor 接口上的 getGround() 方法仅指定为 return Ground<O, J>,[=14= 的子类型仍然可能] 将方法专门化为 return 更具体的子类型。例如,这是完全合法(且安全)的:

interface Descriptor<O, J> {
    Ground<O, J> getGround();
}

public final class FooDescriptor<O, J> implements Descriptor<O, J> {
    @Override
    public FooGround<O, J> getGround() {
        ...
    }
}

只有当您想要在 FooDescriptorFooGround 之间强制执行某种关系时才会出现问题。这将需要 traits 系统,Java 没有,或者更高种类的类型约束,Java 的类型系统不支持.所以如果你真的需要 FooDescriptorFooGround 之间的关系,你需要添加另一个类型参数来关联它们。但是,如果您并不严格需要它们之间的关系,请不要通过尝试对一个进行编码来使您的类型复杂化。

请注意,此问题通常称为 "parallel inheritance hierarchy" 问题,并且在 SoftwareEngineering.SE 上有很多关于它的现有问题,例如 this one