Annotation-based initial analysis - a warning appears only with an array parameter

I get the following (confusing) warning when using null analysis based on annotations in which an array is involved:

Null type safety (type annotations): The expression of type 'int[]' needs unchecked conversion to conform to 'int @Nullable[]'

This happens when I pass unannotated int[] to the int @Nullable[] parameter.

It is surprising. Usually this is only a problem if you accept a value with a null value and try to pass it to the nonnull parameter, but I do the opposite - taking a known non-zero (albeit not annotated) array and passing it to the method ( Arrays.equals ), which takes null values.

Also this is not a problem with objects without an array. Typically, a variable or return (unannotated, non-array) type T can be assigned to @Nullable T

So my question is , why does this change when T is an array type ?


My class is a hashable / peer proxy for a native class that uses int[] (taken from a C ++ function) as a unique identifier. My package uses annotation-based null analysis, and a third-party package (with a native class) does not.

Here's a drop down version that shows the problem:

TagKey.java:

 package example; import java.util.Arrays; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import other.Tag; import com.google.common.collect.Interner; import com.google.common.collect.Interners; public class TagKey { private TagKey(Tag tag) { this.tag = tag; } public static TagKey forTag(Tag tag) { TagKey candidateKey = new TagKey(tag); return INTERNER.intern(candidateKey); } @Override public int hashCode() { return Arrays.hashCode(this.tag.getUniqueIdentifier()); } @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } else if (obj == null) { return false; } else if (getClass() != obj.getClass()) { return false; } else { return ((TagKey) obj).hasMatchingIdentifier(this.tag.getUniqueIdentifier()); // Warning appears here } } public boolean hasMatchingIdentifier(int @Nullable[] id) { return Arrays.equals(this.tag.getUniqueIdentifier(), id); } @SuppressWarnings("null") // Guava cannot use type parameter annotations due to backward compatibility with Java 6 private static final Interner<TagKey> INTERNER = Interners.newWeakInterner(); private final Tag tag; } 

package-info.java:

 /** * @author finnw * */ @org.eclipse.jdt.annotation.NonNullByDefault package example; 

Tag.java: (partial)

 package other; public class Tag { public native int[] getUniqueIdentifier(); // Currently returns a fresh array on each call, but may change in the near future to reuse a single array } 

The workaround I'm currently using is:

  public boolean hasMatchingIdentifier(@Nullable Object id) { return id instanceof int[] && Arrays.equals(this.tag.getUniqueIdentifier(), (int[])id); } 

I would prefer to avoid these approaches:

  • Adding @SuppressWarnings("null") to TagKey or its equals method (the production version is a bit more complicated and has a high risk of embarrassing NPE.)

Notes:

  • My JDT version is 3.10.0.v20140606-1215
  • I made a mistake @Nullable int[] id declaring @Nullable int[] id . Surprisingly, there was no warning message for this, although this implies that the primitive int elements may be null , which is clearly incorrect.
+5
source share
2 answers

Add this method:

 @SuppressWarnings("null") public static int @Nullable[] getUniqueIdentifier(Tag tag) { return tag.getUniqueIdentifier(); } 

then

 return ((TagKey) obj).hasMatchingIdentifier(getUniqueIdentifier(this.tag)); 

That's why I ignore the warning “unchecked conversion from non-annotated” until the downlink profiles are supported, you get stuck or suppress unnecessary warnings everywhere (which defeats the point), or create annotated wrapper methods for each library.

+1
source

Any array checked by Arrays.equals can process zeros. You were right to say @Nullable int [] id, because @Nullable does not determine that the elements in the array are NULL, it determines that the parameter - an object reference - can be null. Arrays.equals (null, null) returns true.

 private int[] a = null; 

- quite a reasonable code. So what you are checking is an array reference match, which is true if all elements match each other or if both links are zero. If a variable is declared as int [], its internal members cannot, by definition, be null. That is, the above code is legal, it is not:

 a[0] = null; 

It would be different if it were defined as Integer [], but it is not.

0
source

Source: https://habr.com/ru/post/1201087/


All Articles