1

Why does the following code cause a warning when I try to compile it?

Bag.java:

import java.util.List;

public interface Bag<T> {
  public List<String> getOwnerNames();
}

BagException.java:

public class BagException extends Exception {

    public BagException(Bag badBag) {
        super(buildMessage(badBag));
    }

    private static String buildMessage(Bag badBag) {

        //   BagException.java:10: warning: [unchecked] unchecked conversion
        //                   List<String> owners = badBag.getOwnerNames();
        //                                                             ^
        //     required: List<String>
        //     found:    List
        List<String> owners = badBag.getOwnerNames();

        return "Something went wrong with the bag of " + String.join(", ", owners);
    }

}

List<String> owners = badBag.getOwnerNames() in buildMessage causes the warning even though the method is declared to return List<String>.

It seems really odd that these changes make the warning disappear:

  • Using Bag<Integer> badBag instead of Bag badBag in argument of buildMessage:

    private static String buildMessage(Bag<Integer> badBag) {

  • Using Bag instead of Bag<T> in interface:

    public interface Bag {

I was expecting the method getOwnerNames to be independant of the type T of Bag. It would be helpful if you could answer one of these questions:

  • Why do I get a warning?
  • What can I do about it other than suppressing it?
Markus
  • 23
  • 3
  • Regarding the duplicate: Now I see that I have been searching for the wrong terms. I should have looked for "raw type" or "wildcard". – Markus Sep 23 '15 at 11:38

2 Answers2

1

You are using a so-called raw type in your program by declaring Bag without an argument for the class's type parameter. The Java compiler javac treats raw types such as if it did not contain generic type information. This concerns any generic type that is part of the raw type, even such types that do not contain a type variable of the raw type. (In your case, List<String> is treated as a raw List.)

As a consequence, all generic types' in the rawly referenced Bag are treated as erasures. You can also observe that

String value = badBag.getOwnerNames().get(0);

would not compile as the raw Bag returns a raw List and not a List<String>. The warning you receive adresses the fact that you assign such a raw list to a generic List<String> what can lead to heap pollution.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • +1 Interestingly, the auto-completion on IntelliJ says that `getOwnerNames()` returns a `List` while if you chain the operations (`get/add/...`), it clearly shows that it returned a raw list. – Alexis C. Sep 22 '15 at 13:31
  • Unfortunately, IDEs often get generic types wrong. Many IDEs apply a sort of "partial erasure" where as much generic type information as possible is erased. However, javac does not add bridge methods when extending raw types what can mess up the polymorphism. – Rafael Winterhalter Sep 22 '15 at 13:37
  • This answer nicely describes what the problem is. I could have figured out the solution (using the wildcard `>`) with this information and the referenced quetion. This is why I am accepting this answer. – Markus Sep 23 '15 at 11:39
-1

What can I do about it other than suppressing it?

Try:

public BagException(Bag<?> badBag) {
    super(buildMessage(badBag));
}

private static String buildMessage(Bag<?> badBag) {
Puce
  • 37,247
  • 13
  • 80
  • 152