Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange error on list initialization

Tags:

java

generics

A friend of mine asked me if I could help him find out the reason behind an error he is getting on a piece of code and most importantly why the error disappears when he add some piece of code. I've looked into the docs about the classes and couldn't find out the reason too.

Here is the code:

import java.util.Arrays;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

public class Test {

    public static void main(String[] args) {
        /**
        * This line shows this compilation error in eclipse:
        * Type mismatch: cannot convert from 
        *     List<Class<? extends JComponent&Accessible>> 
        *          to List<Class<? extends JComponent>>
        */
        List<Class<? extends JComponent>> listComp = Arrays.asList(JTabbedPane.class, 
                                                                   JPanel.class);

        /**
        * This one compiles fine and the difference is that
        * he added JComponent.class on the list and the 
        * code is working fine
        */
        List<Class<? extends JComponent>> listComp2 = Arrays.asList(JTabbedPane.class, 
                                                                    JPanel.class, 
                                                                    JComponent.class);
    }

}

As you can see, the only difference is that on the second list variable we have added the JComponent.class and the error goes away.

Why is that?

Update

We are using Java JDK 7 update 80

Update 2

Another friend suggested this piece of code and it also works:

List<Class<? extends JComponent>> listComp = 
      new ArrayList<Class<? extends JComponent>> ( Arrays.asList( JTabbedPane.class, 
                                                                  JPanel.class));
like image 875
Jorge Campos Avatar asked Jul 24 '15 19:07

Jorge Campos


1 Answers

I can duplicate this issue in Java 7, but when I switch to Java 8, the error is no longer given. Also, if I stay in Java 7, but I give a type argument to Arrays.asList, the error is no longer given.

List<Class<? extends JComponent>> listComp =
    Arrays.<Class<? extends JComponent>>asList(JTabbedPane.class, JPanel.class);

Java will attempt to determine the target type of the return of Arrays.asList. Apparently, both JTabbedPane and JPanel extend JComponent and implement Accessible. However, JComponent doesn't implement Accessible. The result is the most complete, specific type the compiler can get in common from all of the types. For your first example, the inferred type is List<Class<? extends JComponent & Accessible>>, because both arguments fit that type. When you add JComponent, the inferred type is now List<Class<? extends JComponent>> and that matches listComp, allowing it to compile in Java 7.

Java 8 features improved target type inference. That tutorial's example is of Collections.emptyList being passed to a method requiring a List<String>. In Java 7, the inferred type would be List<Object> and a compilation error would occur. However, in Java 8, the inferred type is List<String>, matching the method's parameter, and the code compiles.

This isn't quite the same example as your code, but it's close enough that I believe that in general, Java 8's improved target type inference explains why the code compiles with that version.

like image 94
rgettman Avatar answered Nov 02 '22 20:11

rgettman