Initialization Double Brace Type Confusion

I thought it would be relatively easy, but alas, it seems that it is not.

I am currently writing Unit-Tests for a facade-like structure in my project using Java EE 6.
For tests, I use Junit 4.11, and Eclipse Kepler as an IDE.

From what I see, it seems that something is β€œwrong” with the initialization of the double bracket, but I am not knowledgeable enough to put my finger on why it does not work, as I think it follows.

To achieve this, I use the following class to convert to a centralized location:

package com.example-company.util.converters; import java.util.HashMap; import java.util.Map; import com.example-company.model.Location; import com.example-company.model.Right; public final class ModelConverters { private static final Map<Class<?>, ModelConverter<?, String>> modelConverterBacking = new HashMap<Class<?>, ModelConverter<?, String>>(); static { modelConverterBacking.put(Right.class, new RightConverter()); modelConverterBacking.put(Location.class, new LocationConverter()); }; public static <T> String convert(final T input) throws IllegalStateException { @SuppressWarnings("unchecked") ModelConverter<T, String> modelConverter = (ModelConverter<T, String>) modelConverterBacking .get(input.getClass()); if (modelConverter == null) { throw new IllegalStateException("No mapping found for " + input.getClass()); } return modelConverter.convertToView(input); } } 

As far as this happens, he mainly plays with generics and a static map. Now I decided to write some block tests for this. The following class is slightly reduced, all test cases that do not reproduce the problem have been removed.

 package com.example-company.test.unit.util.converters; import static org.junit.Assert.assertEquals; import com.example-company.model.Location; import com.example-company.util.converters.ModelConverters; import org.junit.Test; public class ModelConvertersFacadeTests { @Test public void test_MappingForLocationExists() { final Location stub = new Location() { { setLocationName(""); } }; String actual = ModelConverters.convert(stub); assertEquals("", actual); } } 

So far so good, nothing should happen, at least not what I got now. And this: Unusual IllegalStateException with the following glass:

 java.lang.IllegalStateException: No mapping found for class com.example-company.test.unit.util.converters.ModelConvertersFacadeTests$1 at com.example-company.util.converters.ModelConverters.convert(ModelConverters.java:23) at com.example-company.test.unit.util.converters.ModelConvertersFacadeTests.test_MappingForLocationExists(ModelConvertersFacadeTests.java:24) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 

The first thing I did was run it again, and then set a breakpoint to check what was going on inside ModelConverters#convert()

The fact that I have slightly collapsed on me:

Debug Perspective: Expressions

input.getClass() ModelConvertersFacadeTests return ModelConvertersFacadeTests . But why doesn't it return com.example-company.model.Location ?

Full Debug Perspective, names censored

+6
source share
1 answer

Input.getClass () seems to return ModelConvertersFacadeTests

This is not true. Your stacktrace says this is a class:

com.example-company.test.unit.util.converters.ModelConvertersFacadeTests $ 1

Pay attention to $1 at the end. This means that your class is anonymous (it does not have a proper name) of the inner class.

this$0 , which we see in your screenshot, is just a link to an external class.

Each time you make new SomeClass() { ... } , you create an anonymous inner class.

The initialization of the double bracket has nothing to do with this. Each time you use double-bracket initialization, you also create an anonymous inner class.


Solution by searching the map in different ways

Your Map has a mapping for Right.class and Location.class , but it doesn't have a mapping for subclasses of these two classes.

 static { modelConverterBacking.put(Right.class, new RightConverter()); modelConverterBacking.put(Location.class, new LocationConverter()); }; 

What you could do (not to say that this is the best approach) is to scroll through the keys of your card and check:

 mapKey.isAssignableFrom(input.getClass()) 

When this returns true, you know that you either have a mapKey class, or you have a subclass of it.

Instead of iterating over the map keys, you can also iterate over the superclasses and implemented interfaces of the object you are going to, and search for modelConverterBacking.get for each of them. The effect will be the same.


Solution without using an anonymous inner class

Your current code is:

 final Location stub = new Location() { { setLocationName(""); } }; 

If you do this:

 final Location stub = new Location(); stub.setLocationName(""); 

Then you will not create an anonymous inner class and therefore will not have this problem.

However, even if you just do this:

 final Location stub = new Location() {}; stub.setLocationName(""); 

Then you have an anonymous inner class that will cause problems for you.


Very important do not mix the two classes ModelConvertersFacadeTests$1 and ModelConvertersFacadeTests .

+13
source

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


All Articles