Unchecked assignment warning
Asked Answered
U

2

41

I am using Android Studio 1.1.0.

This causes no warning:

public static class A {
    public Map<Integer, String> getMap() {
        return null;
    }
}

public static class B {
    public void processA(A a) {
        Map<Integer, String> map = a.getMap();
    }
}

But make A generic:

public static class A<T> {
    public Map<Integer, String> getMap() {
        return null;
    }
}

And this line:

Map<Integer, String> map = a.getMap();

gets you a warning now: "Unchecked assignment: 'java.util.Map to java.util.Map<java.lang.Integer, java.lang.String>'.

Even though the signature of getMap is totally independent of T, and the code is unambiguous regarding the types the Map contains.

I know that I can get rid of the warning by reimplementing processA as follows:

public <T> void processA(A<T> a) {
    Map<Integer, String> map = a.getMap();
}

But why would I have to do that? What does T matter here at all?

So, the question is - why does type erasure have to not only affect T (which is understandable - if I'm passing an instance of A, T is an unknown), but also "hardcoded" generic signature like <Integer, String> in this case?

Urethroscope answered 2/3, 2015 at 10:56 Comment(0)
A
26

In your second case when you do:

public void processA(A a)

What do you mean by A? Does it mean A<String> or A<List<String>> or what? You might not be using anything related to type of A, but hey the compiler doesn't know this fact. To compiler, just A is a sign of panic.

In your case, because you dont specifically need to know the type of A, you can:

public void processA(A<?> a) {
    Map<Integer, String> map = a.getMap();
} 

Having an argument type of A<?> means, you do not specifically care the type of A and just specify a wild card. To you it means: any object of A with any type as its generic type would do. In reality, it means you do not know the type. Its useless because you cannot do anything related to A in typesafe manner as ? can be virtually anything!

But as per your method body, it makes all the sense in the world to use A<?> because no where in the body you actually need the type of A

Anthonyanthophore answered 2/3, 2015 at 11:43 Comment(6)
You might not be using anything related to type of A, but hey the compiler doesn't know this fact - well, it could get to know it quite easily, just take a look at the implementation of processA, it's not hard to verify that T is irrelevant ;) I'll go with <?> as you suggest, but I'm disappointed with the compiler.Urethroscope
@KonradMorawski In an ideal world, just A should be illegal and the code shouldn't compile. But it is allowed for backward compatibility reasons. For a class declared as A<T>, its generic type is extremely important to do anything related to A. So just using A should not be allowed (in fact in Scala, it is illegal to use just A)Anthonyanthophore
Yeah, I imagine... I don't use Scala, but I come from the C# side of the world and Java's generics feels like wearing shoes too small.Urethroscope
@KonradMorawski Moreover it is practically impossible for the compiler to know it. You might be doing reflection or doing a cast and working along. It is plainly extremely difficult to suggest with 100% conviction, that the type is not used.Anthonyanthophore
@KonradMorawski Well agreed :). But in java world, backward compatibility >>>> anything else. So it is how it is :)Anthonyanthophore
@Anthonyanthophore - Thanks for this answer. This is first explanation of wildcards that has made clear sense to me. (Perhaps because I had run into the exact issue of the questioner, where I had a parameterized class with an explicitly parameterized method which warned of an unchecked assignment on usage.)Harrell
E
11

When you mean to accept an A<T> of any possible type T, but don't need the T, this is correctly expressed by using a wildcard and writing A<?>. Doing so will get rid of the warning in your code:

public void processA(A<?> a) {
    Map<Integer, String> map = a.getMap();
}

Using the bare type A is not treated equivalently. As explained in the Java Language Specification, raw types like that are not intended to be used in new code:

Unchecked conversion is used to enable a smooth interoperation of legacy code, written before the introduction of generic types, with libraries that have undergone a conversion to use genericity (a process we call generification). In such circumstances (most notably, clients of the Collections Framework in java.util), legacy code uses raw types (e.g. Collection instead of Collection<String>). Expressions of raw types are passed as arguments to library methods that use parameterized versions of those same types as the types of their corresponding formal parameters.

Such calls cannot be shown to be statically safe under the type system using generics. Rejecting such calls would invalidate large bodies of existing code, and prevent them from using newer versions of the libraries. This in turn, would discourage library vendors from taking advantage of genericity. To prevent such an unwelcome turn of events, a raw type may be converted to an arbitrary invocation of the generic type declaration to which the raw type refers. While the conversion is unsound, it is tolerated as a concession to practicality. An unchecked warning is issued in such cases.

Evaluate answered 2/3, 2015 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.