有时在查看代码时,我会看到许多方法指定了注释:
@SuppressWarnings("unchecked")
这是什么意思?
有时在查看代码时,我会看到许多方法指定了注释:
@SuppressWarnings("unchecked")
这是什么意思?
有时 Java 泛型只是不允许您做您想做的事,您需要有效地告诉编译器您正在做的事情在执行时确实是合法的。
当我模拟一个通用接口时,我通常会觉得这很痛苦,但也有其他例子。通常值得尝试找出一种避免警告而不是抑制警告的方法(Java 泛型常见问题解答在此处提供帮助),但有时即使有可能,它也会使代码变形太多,以至于抑制警告更加整洁。在这种情况下,请务必添加解释性评论!
同一个泛型常见问题解答有几个关于这个主题的部分,从“什么是‘未检查’警告?”开始。- 非常值得一读。
这也可能意味着当前的 Java 类型系统版本不足以满足您的情况。有几个JSR 命题/hacks 来解决这个问题:类型令牌、超级类型令牌、Class.cast()。
如果您真的需要这种抑制,请尽可能缩小范围(例如,不要将其放在类本身或长方法上)。一个例子:
public List<String> getALegacyListReversed() {
@SuppressWarnings("unchecked") List<String> list =
(List<String>)legacyLibrary.getStringList();
Collections.reverse(list);
return list;
}
简单地说:这是一个警告,编译器表明它不能确保类型安全。
JPA服务方法例如:
@SuppressWarnings("unchecked")
public List<User> findAllUsers(){
Query query = entitymanager.createQuery("SELECT u FROM User u");
return (List<User>)query.getResultList();
}
如果我没有在此处注释 @SuppressWarnings("unchecked"),那么我想在其中返回我的 ResultList 的行会有问题。
简而言之,类型安全意味着:如果程序编译时没有错误和警告,并且在运行时没有引发任何意外的 ClassCastException ,则该程序被认为是类型安全的。
我建立在http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html
SuppressWarning注释用于禁止对带注释的元素发出编译器警告。具体来说,该unchecked
类别允许抑制由于未经检查的类型转换而生成的编译器警告。
在 Java 中,泛型是通过类型擦除来实现的。例如下面的代码。
List<String> hello = List.of("a", "b");
String example = hello.get(0);
编译为以下。
List hello = List.of("a", "b");
String example = (String) hello.get(0);
并List.of
定义为。
static <E> List<E> of(E e1, E e2);
类型擦除后变为哪个。
static List of(Object e1, Object e2);
编译器在运行时不知道什么是泛型类型,所以如果你写这样的东西。
Object list = List.of("a", "b");
List<Integer> actualList = (List<Integer>) list;
Java 虚拟机在运行程序时不知道泛型类型是什么,所以它编译并运行,对于 Java 虚拟机,这是List
类型转换(这是它唯一可以验证的东西,所以它只验证那个)。
但现在添加这一行。
Integer hello = actualList.get(0);
并且 JVM 会抛出一个意想不到的ClassCastException
,因为 Java 编译器插入了一个隐式转换。
java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
警告告诉程序员强制unchecked
转换可能会导致程序在其他地方抛出异常。抑制警告@SuppressWarnings("unchecked")
告诉编译器程序员认为代码是安全的并且不会导致意外异常。
你为什么想这么做?Java 类型系统不足以代表所有可能的类型使用模式。有时你可能知道强制转换是安全的,但 Java 并没有提供这样说的方法——可以@SupressWarnings("unchecked")
使用隐藏这样的警告,以便程序员可以专注于真正的警告。例如,Optional.empty()
返回一个单例以避免分配不存储值的空选项。
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
此强制转换是安全的,因为无法检索存储在空选项中的值,因此不存在意外的类强制转换异常的风险。
您可以禁止编译器警告并告诉泛型您编写的代码根据它是合法的。
例子:
@SuppressWarnings("unchecked")
public List<ReservationMealPlan> retreiveMealPlan() {
List<ReservationMealPlan> list=new ArrayList<ReservationMealPlan>();
TestMenuService testMenuService=new TestMenuService(em, this.selectedInstance);
list = testMenuService.getMeal(reservationMealPlan);
return list;
}
一个技巧是创建一个扩展通用基本接口的接口......
public interface LoadFutures extends Map<UUID, Future<LoadResult>> {}
然后你可以在演员之前用 instanceof 检查它......
Object obj = context.getAttribute(FUTURES);
if (!(obj instanceof LoadFutures)) {
String format = "Servlet context attribute \"%s\" is not of type "
+ "LoadFutures. Its type is %s.";
String msg = String.format(format, FUTURES, obj.getClass());
throw new RuntimeException(msg);
}
return (LoadFutures) obj;
据我所知,目前它与禁止泛型警告有关。泛型是 JDK 5 之前的 JDK 版本不支持的新编程结构,因此任何旧结构与新结构的混合都可能会产生一些意想不到的结果。
编译器会警告程序员,但如果程序员已经知道,他们可以使用 SuppressWarnings 关闭那些可怕的警告。
编译器指示它不能确保类型安全的警告。术语“未经检查的”警告具有误导性。这并不意味着以任何方式未检查警告。术语“未检查”是指编译器和运行时系统没有足够的类型信息来执行确保类型安全所必需的所有类型检查。从这个意义上说,某些操作是“未经检查的”。
“未经检查”警告的最常见来源是使用原始类型。当通过原始类型变量访问对象时会发出“未检查”警告,因为原始类型没有提供足够的类型信息来执行所有必要的类型检查。
示例(未经检查的警告与原始类型一起使用):
TreeSet set = new TreeSet();
set.add("abc"); // unchecked warning
set.remove("abc");
warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.TreeSet
set.add("abc");
^
当调用 add 方法时,编译器不知道将 String 对象添加到集合中是否安全。如果 TreeSet 是包含 String s(或其超类型)的集合,那么它将是安全的。但是从原始类型 TreeSet 提供的类型信息,编译器无法分辨。因此,该调用可能是不安全的,并且会发出“未经检查”的警告。
当编译器发现目标类型是参数化类型或类型参数的强制转换时,也会报告“未检查”警告。
示例(未经检查的警告与强制转换为参数化类型或类型变量):
class Wrapper<T> {
private T wrapped ;
public Wrapper (T arg) {wrapped = arg;}
...
public Wrapper <T> clone() {
Wrapper<T> clon = null;
try {
clon = (Wrapper<T>) super.clone(); // unchecked warning
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
try {
Class<?> clzz = this.wrapped.getClass();
Method meth = clzz.getMethod("clone", new Class[0]);
Object dupl = meth.invoke(this.wrapped, new Object[0]);
clon.wrapped = (T) dupl; // unchecked warning
} catch (Exception e) {}
return clon;
}
}
warning: [unchecked] unchecked cast
found : java.lang.Object
required: Wrapper <T>
clon = ( Wrapper <T>)super.clone();
^
warning: [unchecked] unchecked cast
found : java.lang.Object
required: T
clon. wrapped = (T)dupl;
如果涉及运行时的动态类型检查,则目标类型为(具体或有界通配符)参数化类型或类型参数的强制转换是不安全的。在运行时,只有类型擦除可用,而不是源代码中可见的确切静态类型。因此,转换的运行时部分是基于类型擦除执行的,而不是基于确切的静态类型。
在示例中,对 Wrapper 的强制转换将检查从 super.clone 返回的对象是否是 Wrapper ,而不是检查它是否是具有特定类型成员的包装器。类似地,类型参数 T 的强制转换在运行时被强制转换为 Object 类型,并且可能完全优化掉了。由于类型擦除,运行时系统无法在运行时执行更有用的类型检查。
在某种程度上,源代码具有误导性,因为它建议执行到相应目标类型的转换,而实际上转换的动态部分仅检查目标类型的类型擦除。发出“未检查”警告是为了提醒程序员注意演员表的静态和动态方面之间的这种不匹配。
请参考:什么是“未经检查”的警告?
@SuppressWarnings 注解是 JDK 中可用的三个内置注解之一,在 Java 1.5 中与 @Override 和 @Deprecated 一起添加。
@SuppressWarnings 指示编译器忽略或抑制注释元素中指定的编译器警告以及该元素内的所有程序元素。例如,如果一个类被注释为抑制特定的警告,那么在该类内部的方法中生成的警告也将被分离。
您可能已经看过@SuppressWarnings("unchecked") 和@SuppressWarnings("serial"),这是@SuppressWarnings 注释的两个最流行的例子。前者用于抑制由于未经检查的强制转换而产生的警告,而后者的警告用于提醒在 Serializable 类中添加 SerialVersionUID。