Generics are weird, or I am weird
Yesterday, late afternoon, I stumbled across a compilation problem for one of my unit tests in Java. More specifically, compilation in Eclipse seemed to work fine but using Maven2 and the java compiler included in version 1.6.0_14 it complained about not being able to find the proper method.
Given the two classes below (modified for brevity, of course):
javaimport java.util.*;public class Foo<t> {private List> children = new ArrayList ();public List> getChildren() { return children; }public Foo addChild(Foo child) { children.add(child); }}
javaimport java.util.*;public class Foo<t> {private List> children = new ArrayList ();public List> getChildren() { return children; }public Foo addChild(Foo child) { children.add(child); }}
javaimport static org.hamcrest.Matchers.*;import static org.junit.Assert.*;import org.junit.*;public class FooTest {Foo parent, child;@Beforepublic void setUp (){parent = new Foo ();child = new Foo ();parent.addChild(child);}@Testpublic void childAdded(){assertThat(parent.getChildren(), hasItem(child));}}
javaimport static org.hamcrest.Matchers.*;import static org.junit.Assert.*;import org.junit.*;public class FooTest {Foo parent, child;@Beforepublic void setUp (){parent = new Foo ();child = new Foo ();parent.addChild(child);}@Testpublic void childAdded(){assertThat(parent.getChildren(), hasItem(child));}}
For some reason it seems that omitting the type for Foo at line 7 makes Java drop the type of the getChildren method. The compiler will infer the return type of that method as a List (no generics). The type of the hasItem method from the hamcrest matchers, in line 18, is typed as Matcher. The result, during compilation the compiler will complain about line 18 in the test class. My current settings in Eclipse show this unchecked invocation as a warning. Nevertheless it compiles and runs. The standard Java compiler is very strict in this matter.
The quick solution is simple, just add a type to line 7 or do a cast at line 18. Doing both is overkill, but works too. Adding the type at line 7 is the best choice, because that way there is no need to explicitly cast every call to getChildren where this problem occurs.
javaimport static org.hamcrest.Matchers.*;import static org.junit.Assert.*;import java.util.*;import org.junit.*;public class FooTest {Foo <Object> parent, child;@Beforepublic void setUp (){parent = new Foo ();child = new Foo ();parent.addChild(child);}@Testpublic void childAdded(){assertThat((List<Object>) parent.getChildren(), hasItem(child));}}
javaimport static org.hamcrest.Matchers.*;import static org.junit.Assert.*;import java.util.*;import org.junit.*;public class FooTest {Foo <Object> parent, child;@Beforepublic void setUp (){parent = new Foo ();child = new Foo ();parent.addChild(child);}@Testpublic void childAdded(){assertThat((List<Object>) parent.getChildren(), hasItem(child));}}
There might be a way to avoid this inconvenience by some simple changes to the Foo source code, changes I haven't discovered yet. If there are any then I will find it!
- ← Previous
My opinion on using Scr.im - Next →
What the... interrobang‽