Search code examples
javaintellij-ideasuppress-warnings

Why unchecked warning would not be suppressed


I have several unchecked warnings that I would like to suppress but on run I still see this message:

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

I am using the following type of annotation and indeed the warning messages disappear in the IDE source code.

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

I am distributing my code as a public API and I am wondering if the users would see the message too during its usage.

I am testing my code with junit. I am using Java 8 and intelliJ 2016.3.6.

I have checked the -Xlint:unchecked suggestions details. There are no more un-annotated parts in the code but still the compiler suggestions does not disappear (or decrease).

EDIT

to better contextualize one of the warning I am getting, here a simplified, but still relevant, part of the code:

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();
}

Here the complete message about it:

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

I appreciate suggestions on interface designing but my question is related on why the warning does not get suppressed.


Solution

  • Your problem actually lies with the definition of getGround() in Descriptor. The type variable I is free in the declaration of getGround(), meaning that your method is promising to return whatever type the caller chooses! The only way to return a value of type I is by subverting the type system somehow (e.g. by throwing an exception or by returning null).

    The compiler correctly detects that I in the caller's use of getGround() may be a different type than J in the implementation of getGround() in MORBase, but the compiler is not able to emit any bytecode to check the type (since you're compiling MORBase, but the bytecode would need to be inserted into callers of getGround()). Since it cannot check the type, and cannot insert code to check the type, it instead correctly emits an unchecked warning.

    It may be possible to actually suppress this warning by attaching the @SuppressWarnings annotation to the declaration of getGround() in the Descriptor interface, but you really shouldn't do that. Instead, fix your code.

    My recommended fix would be to simply drop the type variable I in the declaration of getGround(), and rely on subtype polymorphism to allow you to simply declare getGround() as returning Ground<O, J>:

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

    If that's not possible, and you need to be able to return subtypes of Ground in subtypes of Descriptor, then you need to add a type parameter to Descriptor to ensure the type is correctly propagated to the caller:

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

    Note that, even if the getGround() method on the Descriptor interface is specified to just return Ground<O, J>, it's still possible for subtypes of Descriptor to specialize the method to return a more specific subtype. For example, this is totally legal (and safe):

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

    The problem only arises if you want to enforce some relationship between FooDescriptor and FooGround. That would require either a traits system, which Java doesn't have, or a higher-kinded type constraint, which Java's type system does not support. So if you really need a relationship between FooDescriptor and FooGround, you need to add another type parameter to associate them. But if you don't strictly need a relationship between them, don't complicate your types by trying to encode one.

    Note that this problem is commonly called the "parallel inheritance hierarchy" problem, and there are plenty of existing questions about it on SoftwareEngineering.SE, like this one.