2014-09-01 15 views
6

Tôi nghĩ điều này sẽ tương đối dễ dàng, nhưng than ôi, có vẻ như không phải vậy.Double Brace initialization Type Confusion

Tôi hiện đang viết đơn vị xét nghiệm cho một cấu trúc cho mặt tiền giống như trong dự án của tôi sử dụng Java EE 6.
Đối với thử nghiệm tôi sử dụng Junit 4.11, với Eclipse Kepler như IDE. Từ những gì tôi có thể thấy, có vẻ như có một cái gì đó "sai" với khởi động đôi cú đúp, nhưng tôi không đủ hiểu biết để đặt ngón tay của tôi trên tại sao nó không hoạt động như tôi nghĩ.

Để có được đến thời điểm này, tôi đang sử dụng lớp sau đây để làm cho chuyển đổi trong một nơi tập trung:

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); 
    } 
} 

Theo như này đi nó chủ yếu là chơi với Generics và một bản đồ tĩnh. Bây giờ tôi quyết định tôi nên viết một vài bài kiểm tra đơn vị cho điều đó. Các lớp sau đây là một chút rút ngắn, tất cả các trường hợp thử nghiệm mà không tái sản xuất vấn đề đã được gỡ bỏ.

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); 
    } 
} 

Cho đến giờ rất tốt, không có gì thực sự xảy ra, ít nhất là không phải những gì tôi có bây giờ. Và đó là: Một fancy IllegalStateException với stacktrace sau:

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) 

Điều đầu tiên tôi đã làm, đã chạy nó một lần nữa, và sau đó thiết lập một breakpoint để kiểm tra những gì xảy ra bên trong ModelConverters#convert()

Những gì tôi đã hơi flabberghasted tôi:

Debug Perspective: Expressions

dường như input.getClass() re quay ModelConvertersFacadeTests. Nhưng tại sao nó không trả lại com.example-company.model.Location?

Full Debug Perspective, names censored

+0

[Bạn thường nên cảnh giác với việc sử dụng dấu ngoặc kép kép] (http://stackoverflow.com/q/924285/521799) –

Trả lời

13

Dường như input.getClass() trả về ModelConvertersFacadeTests

Đó không phải là sự thật. stacktrace bạn nói đây là lớp:

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

Lưu ý $1 ở cuối. Điều này có nghĩa rằng lớp của bạn là một lớp bên trong vô danh (nó không có tên của riêng nó).

this$0 mà chúng tôi thấy trong ảnh chụp màn hình của bạn chỉ là một tham chiếu cho lớp bên ngoài.

Mỗi khi bạn làm new SomeClass() { ... } bạn đang tạo một lớp bên trong ẩn danh.

Khởi tạo cú đúp đôi không có gì liên quan đến việc này.Mỗi khi bạn đang sử dụng đôi brace mặc dù, bạn cũng đang tạo ra một lớp bên trong vô danh.


Giải quyết bằng cách nhìn lên trong bản đồ khác nhau

bạn Map có một bản đồ cho Right.classLocation.class, nhưng nó không có một bản đồ cho lớp con của hai loại cổ phiếu này.

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

gì bạn thể do (không nói nó là cách tiếp cận tốt nhất), là để lặp qua các phím của bản đồ của bạn và kiểm tra:

mapKey.isAssignableFrom(input.getClass()) 

Khi điều đó trả về true, bạn biết rằng bạn có một lớp là mapKey hoặc bạn có một lớp con của nó.

Thay vì lặp qua các khóa bản đồ, bạn cũng có thể lặp qua các siêu lớp và giao diện được triển khai của đối tượng bạn truyền vào và thực hiện tra cứu modelConverterBacking.get cho từng đối tượng. Hiệu ứng sẽ giống nhau.


Giải quyết mà không sử dụng một lớp bên trong vô danh

mã hiện tại của bạn là:

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

Nếu bạn thay vào đó sẽ làm:

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

Sau đó, bạn không tạo ra bất kỳ lớp bên trong vô danh và do đó sẽ không gặp vấn đề này.

Tuy nhiên, ngay cả khi bạn chỉ cần làm điều này:

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

Sau đó, bạn có một lớp bên trong vô danh, mà sẽ gây rắc rối cho bạn.


Đó là rất quan trọng không trộn lẫn hai lớp ModelConvertersFacadeTests$1ModelConvertersFacadeTests.

Các vấn đề liên quan