1.概述
有时,当我们编译Java源文件时,会看到Java编译器打印的“ unchecked cast
”警告消息。
在本教程中,我们将仔细研究警告消息。我们将讨论该警告的含义,被警告的原因以及如何解决问题。
默认情况下,某些Java编译器会抑制未经检查的警告。
确保在查看此“ unchecked cast
”警告之前,已启用编译器的选项以打印“ unchecked”警告。
2.“ unchecked cast
”警告是什么意思?
“ unchecked cast
”是编译时警告。简而言之,当将原始类型转换为参数类型而不进行类型检查时,我们会看到此警告。
一个例子可以直接解释它。假设我们有一个简单的方法来返回原始类型Map
:
public class UncheckedCast {
public static Map getRawMap() {
Map rawMap = new HashMap();
rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
return rawMap;
}
...
}
现在,让我们创建一个测试方法来调用上面方法的方法,并将结果转换为Map<String, LocalDate>
:
@Test
public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() {
Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMap();
Assert.assertEquals(3, castFromRawMap.size());
Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8));
}
编译器必须允许此强制转换保留与不支持泛型的旧Java版本的向后兼容性。
但是,如果我们编译Java源代码,则编译器将打印警告消息。接下来,让我们使用Maven编译并运行单元测试:
$ mvn clean test
...
[WARNING] .../src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java:[14,97] unchecked cast
required: java.util.Map<java.lang.String,java.time.LocalDate>
found: java.util.Map
...
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
...
[INFO] Results:
[INFO]
[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
如Maven输出所示,我们已成功重现了警告。
另一方面,即使我们看到“ unchecked cast
”编译器警告,我们的测试也没有任何问题。
我们知道编译器不会无故警告我们。当我们看到此警告时,肯定有一些潜在的问题。
让我们弄清楚。
3.为什么Java编译器会警告我们?
尽管我们看到“ unchecked cast
”警告,但我们的测试方法在上一节中运行良好。这是因为当我们将原始类型Map
为Map<String, LocalDate>
,原始Map
仅包含<String, LocalDate>
条目。也就是说,类型转换是安全的。
为了分析潜在的问题,让我们通过向原始类型Map
添加一个条目来稍微更改getRawMap()
方法:
public static Map getRawMapWithMixedTypes() {
Map rawMap = new HashMap();
rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
rawMap.put("date 4", new Date());
return rawMap;
}
这次,我们在上述方法中为<String, Date>
类型的Map
添加了一个新条目。
现在,让我们编写一个新的测试方法来调用getRawMapWithMixedTypes()
方法:
@Test(expected = ClassCastException.class)
public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() {
Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes();
Assert.assertEquals(4, castFromRawMap.size());
Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
}
如果我们编译并运行测试,则会再次打印“ unchecked cast
”警告消息。另外,我们的测试将通过。
但是,由于我们的测试具有expected = ClassCastException.class
参数,所以这意味着测试方法抛出了ClassCastException
。
如果我们仔细研究一下,尽管警告消息指向此行**Map<String, LocalDate>
但在将原始类型Map
为Map<String, LocalDate>
的行上不会抛出ClassCastException
** 。相反,当我们通过键castFromRawMap.get(“date 4”).
获取类型错误的数据时,会发生异常castFromRawMap.get(“date 4”).
如果我们将包含错误类型数据的原始类型集合转换为参数化类型集合,则在加载错误类型的数据之前,不会抛出ClassCastException
。
有时,我们可能太迟才得到例外。
例如,通过调用我们的方法,我们获得了包含许多条目的原始Map
,然后将其转换为参数化类型的Map
:
(Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes()
对于Map
中的每个条目,我们需要将LocalDate
对象发送到远程API。在我们遇到ClassCastException
,很可能已经进行了许多API调用。根据要求,可能会涉及一些额外的还原或数据清除过程。
如果我们能够尽早获得异常,那将是一个好习惯,这样我们就可以决定如何处理类型错误的条目的情况。
当我们了解“ unchecked cast
”警告背后的潜在问题时,让我们看看我们可以采取什么措施来解决该问题。
4.我们应该如何处理警告?
4.1。避免使用原始类型
从Java 5开始引入泛型。如果我们的Java环境支持泛型,则应避免使用原始类型。这是因为使用原始类型将使我们失去泛型的所有安全性和表达优势。
此外,我们应该搜索遗留代码,并将那些原始类型的用法重构为泛型。
但是,有时我们必须使用一些旧库。这些旧外部库中的方法可能返回原始类型集合。
调用这些方法并将其转换为参数化类型将产生“ unchecked cast
”编译器警告。但是我们无法控制外部库。
接下来,让我们看一下如何处理这种情况。
4.2。禁止“ unchecked
”警告
如果我们无法消除“ unchecked cast
”警告,并且确定引起警告的代码是类型安全的,则可以使用SuppressWarnings(“unchecked”)
注释来消除警告。
当使用@ SuppressWarning(“unchecked”)
批注时,应始终将其放在尽可能小的范围内。
让我们以ArrayList
类中的remove()
方法为例:
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
4.3。使用原始类型集合之前进行类型安全检查
如我们@SuppressWarning(“unchecked”)
, @SuppressWarning(“unchecked”)
注释仅抑制警告消息,而没有实际检查@SuppressWarning(“unchecked”)
类型转换是否是类型安全的。
如果不确定转换原始类型是否是类型安全的,则应在实际使用数据之前检查类型,以便更早获得ClassCastException
。
5.结论
在本文中,我们了解了“ unchecked cast
”编译器警告的含义。
此外,我们已经解决了此警告的原因以及如何解决潜在的问题。
0 评论