如何修复:java.lang.ClassCastException: java.util.ArrayList无法转换为double[]

4

当我尝试运行以下代码时,遇到了问题:

当我尝试从 ArrayList<double[]> list 中获取项目时,它抛出了 ClassCastException

方法 list.get(i) 也会抛出异常

public void drawRoutes(ArrayList<String> routes) {
    if (routes.isEmpty()) return;
    for (int i = 0; i < routes.size(); i++) {
        PolylineOptions polylineOptions = new PolylineOptions();
        ArrayList<double[]> list = TransportRoutes.getRoutes(tag, routes).get(i);
        for (double[] points : list) {  // throws ClassCastException
            map.addPolyline(polylineOptions
                    .add(new LatLng(points[0], points[1]))
                    .color(color)
                    .width(POLY_LINE_WIDTH));
        }
    }
}

方法getRoutes():

public static ArrayList<ArrayList<double[]>> getRoutes(String tag, ArrayList<String> numbers) {
    ArrayList<ArrayList<double[]>> routes = new ArrayList<>();
    for (String number : numbers) {
        routes.add(sRoutesHashMap.get(tag).get(number));
    }
    return routes;
}


// sRoutesHashMap is a HashMap<String, HashMap<String, ArrayList<double[]>>>
// and taken from this method


    protected static <T> T getSmth(Context context, String url) {
    T data = null;
    JSONParser jsonParser = new JSONParser(context);
    String json;
    ObjectMapper mapper = new ObjectMapper();
    try {
        json = jsonParser.execute(url).get();
        if (json != null) {
            data = mapper.readValue(json, new TypeReference<T>() {});
        } else return null;
    } catch (InterruptedException | ExecutionException | IOException e) {
        e.printStackTrace();
    }
    return data;
}

完整的堆栈跟踪如下:

java.lang.ClassCastException: java.util.ArrayList cannot be cast to double[]
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.ClassCastException: java.util.ArrayList cannot be cast to double[]
            at in.transee.transee.Drawer.drawRoutes(Drawer.java:46)
            at in.transee.transee.MapsActivity.onCreate(MapsActivity.java:45)
            at android.app.Activity.performCreate(Activity.java:5133)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)

当我使用这种方法时,一切都正常:

public static void getAllRoutes(Context context) {
    String routesJson = sSPData.getString(sCity + ROUTES, EMPTY);
    ObjectMapper mapper = new ObjectMapper();
        JSONParser jsonParser = new JSONParser();
        try {
            routesJson = jsonParser.execute(URL + sCity + SEPARATOR + ROUTES).get().get(0);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    try {
        sRoutesHashMap = mapper.readValue(routesJson,
                new TypeReference<HashMap<String, HashMap<String, ArrayList<double[]>>>>() {});
    } catch (IOException e) {
        e.printStackTrace();
    }
}

ObjectMapper 是哪个包中的? - Andy Brown
com.fasterxml.jackson.databind.ObjectMapper - Michael
你能详细说明一下sRoutesHashMap和getSmth方法之间的关系吗? - antonio
1个回答

3

看起来你遇到了堆污染问题。好消息是,我认为你可以通过以下方式轻松解决它。

protected static <T> T getSmth(TypeReference<T> typeRef, Context context, String url) {
  ...
  data = mapper.readValue(json, typeRef);

并使用以下方式调用:

sRoutesHashMap = getSmth(new TypeReference<HashMapOfBlahBlahBlah>(){}, context, url);

为什么会出现堆污染?

  • mapper.readValue 方法返回的要么是 Object,要么是被标记了 @SuppressWarnings("unchecked")T(我无法确定哪一个,可能是后者)
  • 你将其作为泛型类型进行存储(这就是堆污染的来源)
  • 但在泛型方法中,从 mapper.readValue 返回的 Object 实际上并不是你的泛型类型
  • 这只会在稍后导致 ClassCastException

注意:如果在你的泛型代码或库泛型代码中看到任何地方有 @SuppressWarnings("unchecked"),你应该小心,因为这种情况很可能会发生

如何避免堆污染?

原因可能是因为泛型的 getSmth 方法中的匿名 new TypeReference<T>(){} 无法将类型 T 评估为你的具体类型参数。如果查看 TypeReference 的源代码,由于它使用了 getGenericSuperclassgetActualTypeArguments 的方式,这似乎是有道理的。

一种解决方案是在 getSmth 方法之外创建 TypeReference 的参数化类型,并将其作为参数传递到该方法中。

如何测试堆污染?

如果运行此测试程序,你可能会看到我的意思,而在你的环境上进行此操作将确认这一切:

static TypeFactory typeFactory = TypeFactory.defaultInstance();
public static void main(String[] args) {
    TestRig.<List<String>>pharaoh();                  // oh, that's bad
    TestRig.sam(new TypeReference<List<String>>(){}); // no, that's good
}
public static <T> void pharaoh() {
    TypeReference<?> typeRef = new TypeReference<T>() {};
    JavaType typeT = typeFactory.constructType(typeRef); // this is what happens inside ObjectMapper
    System.out.println("from generic  TypeReference: " + typeRef.getType().toString());
    System.out.println("from generic  TypeReference: " + typeT.toString());
}
public static <T> void sam(TypeReference<T> typeRef) {
    JavaType typeT = typeFactory.constructType(typeRef); // this is what happens inside ObjectMapper
    System.out.println("from concrete TypeReference: " + typeRef.getType().toString());
    System.out.println("from concrete TypeReference: " + typeT.toString());
}

结果:

从泛型 TypeReference: T <--- 噢噢噢
从泛型 TypeReference: [简单类型,类 java.lang.Object]
从具体的 TypeReference: java.util.List <--- 呀呀呀
从具体的 TypeReference: [集合类型; 类 java.util.List,包含 [简单类型,类 java.lang.String]]

参考:Java 教程 > 非可重用类型


这是示例代码的Gist链接 - Andy Brown
感谢您提供全面的答案! - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接