java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap
Posted
技术标签:
【中文标题】java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap【英文标题】:java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to java.util.LinkedHashMap 【发布时间】:2018-06-10 04:35:57 【问题描述】:对于就这个一般性问题提出另一个问题,我深表歉意,但我在 SO 上找到的问题似乎都与我的问题没有密切关系。
我有一个现有的工作数据流管道,它接受 KV<Long, Iterable<TableRow>>
的对象并输出 TableRow
对象。这段代码在我们的生产环境中,运行没有问题。但是,我现在正在尝试使用直接运行器来实现单元测试来测试此管道,但是单元测试在到达该行时会失败
LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);
在管道中,抛出错误信息:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap
现有数据流代码的简化版本如下所示:
public static class Process extends DoFn<KV<Long, Iterable<TableRow>>, TableRow>
/* private variables */
/* constructor */
/* private functions */
@ProcessElement
public void processElement(ProcessContext c) throws InterruptedException, ParseException
EventProcessor eventProc = new EventProcessor();
Processor.WorkItem workItem = new Processor.WorkItem();
Iterator<TableRow> it = c.element().getValue().iterator();
// process all TableRows having the same id
while (it.hasNext())
TableRow item = it.next();
if (item.containsKey(Schema.EVT))
eventProc.process(item, workItem);
else
/* process by different Proc class */
/* do additional logic */
/* c.output() is somewhere far below */
public class EventProcessor extends Processor
// Extract data from an event into the WorkItem
@SuppressWarnings("unchecked")
@Override
public void process(TableRow row, WorkItem item)
try
LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);
LinkedHashMap<String, Object> profile = (LinkedHashMap<String, Object>) row.get(Schema.PROFILE);
/* if no exception, process further business logic */
/* business logic */
catch (ParseException e)
System.err.println("Bad row");
单元测试的相关部分,准备主要输入到Process() DoFn
,如下所示:
Map<Long, List<TableRow>> groups = new HashMap<Long, List<TableRow>>();
List<KV<Long, Iterable<TableRow>>> collections = new ArrayList<KV<Long,Iterable<TableRow>>>();
Gson gson = new Gson();
// populate the map with events grouped by id
for(int i = 0; i < EVENTS.length; i++)
TableRow row = gson.fromJson(EVENTS[i], TableRow.class);
Long id = EVENT_IDS[i];
if(groups.containsKey(id))
groups.get(id).add(row);
else
groups.put(id, new ArrayList<TableRow>(Arrays.asList(row)));
// prepare main input for pipeline
for(Long key : groups.keySet())
collections.add(KV.of(key, groups.get(key)));
导致问题的行是 gson.fromJson(EVENTS[i], TableRow.class);
,它似乎将 TableRow 的内部表示编码为错误的 LinkedTreeMap 类型。
TableRow 的编码类型似乎是 com.google.gson.internal.LinkedTreeMap
,而不是预期的 java.util.LinkedHashMap
。有没有办法可以将单元测试中创建的 TableRow 转换为正确的 java.util.LinkedHashMap
类型,以便单元测试成功,而无需对已经在生产中运行的现有数据流代码进行任何更改?
【问题讨论】:
如果需要更多代码来添加上下文,请告诉我。 为什么需要转换为LinkedHashMap
而不仅仅是Map
?
@RomanPuchkovskiy 老实说,我不确定。 LinkedHashMap
的演员表是以前某个时候的现有代码。将演员表更改为Map
会有什么后果吗?
没错。针对接口编写代码并将LinkedHashMap
替换为Map
。那么你就不需要关心 API 碰巧返回了哪个地图实现。
@Max 如果LinkedTreeMap
是Map
的孩子,那应该不是问题,除非您使用该类的特定内容。但我认为你最好的选择是尝试看看它是否有效
【参考方案1】:
重新发布解决方案作为答案。
如果您不使用具体类的特定功能,则不建议转换为具体类。在这种情况下,最好转换为Map
而不是LinkedHashMap
。 Gson 的LinkedTreeMap
也是Map
,所以应该没有问题。
【讨论】:
【参考方案2】:我会考虑(不仅如此)投code smell。每次对演员表进行编码时,都会冒ClassCastException
发生的风险。
正如其他人已经说过的,Map
接口可以像Map<String, Object> evt = row.get(Schema.EVT);
一样使用。
或者,new LinkedHashMap<String, Object>(row.get(Schema.EVT));
可以构造一个新的LinkedHashMap
。
第二种方法的优点是保留LinkedHashMap
类型,这可能很重要,也可能不重要,这取决于您的情况。
【讨论】:
【参考方案3】:这是因为 LinkedHashMap 并不优于 LinkedTreeMap,因此它们可能没有相同的方法。 Java 编译器因此认为以这种方式进行转换可能会导致evt
具有与row.get(Schema.EVT)
不同的方法,从而导致坏事。
但是,您可以将 LinkedTreeMap 转换为 AbstractMap、Map 或 Object,因为它们都优于它。
所以(正如许多 cmets 指出的那样)要修复它,只需使用
Map<String, Object> evt = row.get(Schema.EVT);
你应该没事的。
【讨论】:
以上是关于java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap 无法转换为 java.util.LinkedHashMap的主要内容,如果未能解决你的问题,请参考以下文章