什么是Java中的SuppressWarnings(“未选中”)?

时间:2009-07-15 06:50:59

标签: java generics unchecked suppress-warnings

有时查看代码时,我看到许多方法指定了注释:

@SuppressWarnings("unchecked")

这是什么意思?

11 个答案:

答案 0 :(得分:386)

有时,Java泛型只是不允许你按照自己的意愿行事,而且你需要有效地告诉编译器你在执行时 所做的事情是合法的。

当我嘲笑通用界面时,我通常会发现这很痛苦,但也有其他例子。通常值得尝试找出一种避免警告而不是抑制它的方法(Java Generics FAQ在这里有帮助)但有时候即使 可能,它也会使代码变形变形,所以抑制警告的东西更整洁。在这种情况下总是添加解释性注释!

相同的泛型FAQ有关于此主题的几个部分,从"What is an "unchecked" warning?"开始 - 非常值得一读。

答案 1 :(得分:48)

它是一个注释,用于禁止有关未经检查的泛型操作(非异常)的编译警告,例如强制转换。它本质上意味着程序员不希望在编译特定代码时通知他已经知道的这些内容。

您可以在此处阅读有关此特定注释的更多信息:

  

SuppressWarnings

此外,Oracle在此提供了一些关于注释用法的教程文档:

  

Annotations

正如他们所说,

  

“在与泛型出现之前编写的遗留代码接口时会发生'未经检查'警告(在标题为泛型的课程中讨论过)。”

答案 2 :(得分:19)

这也可能意味着当前的Java类型系统版本不适合您的情况。有几个JSR propositions / hacks来解决这个问题:输入标记,Super Type Tokens,Class.cast()。

如果你真的需要这种抑制,尽可能缩小范围(例如,不要把它放在课堂本身或长期方法上)。一个例子:

public List<String> getALegacyListReversed() {
   @SuppressWarnings("unchecked") List<String> list =
       (List<String>)legacyLibrary.getStringList();

   Collections.reverse(list);
   return list;
}

答案 3 :(得分:9)

SuppressWarning注释用于抑制带注释元素的编译器警告。具体来说,unchecked类别允许抑制由于未经检查的类型转换而生成的编译器警告。

答案 4 :(得分:8)

简单地说:这是一个警告,编译器指出它无法确保类型安全。

JPA服务方法例如:

@SuppressWarnings("unchecked")
public List<User> findAllUsers(){
    Query query = entitymanager.createQuery("SELECT u FROM User u");
    return (List<User>)query.getResultList();
}

如果我没有在这里分配@SuppressWarnings(&#34;未经检查&#34;),那么就会出现问题,我希望返回我的ResultList。

在快捷方式类型安全意味着:如果程序在没有错误和警告的情况下编译并且在运行时不会引发任何意外的ClassCastException,则该程序被认为是类型安全的。

我建立http://www.angelikalanger.com/GenericsFAQ/FAQSections/Fundamentals.html

答案 5 :(得分:6)

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

当Java编译器插入隐式转换时,JVM将抛出一个意外的ClassCastException

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

此强制转换是安全的,因为无法检索存储在空可选项中的值,因此不存在意外的类强制转换异常的风险。

答案 6 :(得分:5)

您可以禁止编译器警告,并告诉泛型,您编写的代码是合法的。

示例:

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

答案 7 :(得分:5)

一个技巧是创建一个扩展通用基接口的接口......

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;

答案 8 :(得分:3)

据我所知,目前它与抑制有关仿制药的警告有关;泛型是JDK 5之前的JDK版本中不支持的新编程构造,因此旧构造与新构造的任何混合可能会产生一些意想不到的结果。

编译器向程序员发出警告,但如果程序员已经知道,他们可以使用SuppressWarnings关闭那些可怕的警告。

答案 9 :(得分:1)

一个警告,编译器通过该警告表明它无法确保类型安全。 术语“未经检查”的警告具有误导性。这并不意味着警告没有被取消。术语“未检查”是指以下事实:编译器和运行时系统没有足够的类型信息来执行所有确保类型安全所需的类型检查。从这个意义上说,某些操作是“未选中的”。 “未经检查的”警告最常见的来源是原始类型的使用。通过原始类型变量访问对象时发出“未检查”警告,因为原始类型没有提供足够的类型信息来执行所有必要的类型检查。

示例(未经检查的警告与原始类型结合使用):

TreeSet se t = new TreeSet(); set.add("abc"); // unchecked warning set.remove("abc");

警告:[未经检查]以未经检查的方式调用add(E)作为原始类型java.util.TreeSet的成员                set.add(“ abc”);
                      ^ 调用add方法时,编译器不知道将String对象添加到集合是否安全。如果TreeSet是一个包含String(或其超类型)的集合,那么它是安全的。但是根据原始类型TreeSet提供的类型信息,编译器无法分辨。因此,该呼叫可能不安全,并发出“未检查”警告。

当编译器找到目标类型为参数化类型或类型参数的强制类型转换时,也会报告

“未选中”警告。

示例(未经检查的警告以及对参数化类型或类型变量的强制转换):

class Wrapper<T> { private T wrapped ; public Wrapper (T arg) {wrapped = arg;} ... p ublic 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类型,并且可能已完全优化。由于类型擦除,运行时系统无法在运行时执行更有用的类型检查。

在某种程度上,源代码具有误导性,因为它建议执行对相应目标类型的强制转换,而实际上,强制转换的动态部分仅检查目标类型的类型擦除。发出“未经检查”的警告是为了吸引程序员注意演员表的静态和动态方面的这种不匹配。

请参考:What is an "unchecked" warning?

答案 10 :(得分:0)

@SuppressWarnings注释是JDK中提供的三个内置注释之一,并在Java 1.5中与@Override和@Deprecated一起添加。

@SuppressWarnings指示编译器忽略或禁止在带注释的元素和该元素内的所有程序元素中指定的编译器警告。 例如,如果对一个类进行注释以禁止显示特定警告,则在该类内部方法中生成的警告也将被分隔。

您可能已经看到@SuppressWarnings(“未选中”)和@SuppressWarnings(“序列”),这是@SuppressWarnings批注中最受欢迎的两个例子。前者用于抑制由于未经检查的转换而产生的警告,而后一种警告用于提醒有关在Serializable类中添加SerialVersionUID的信息。

了解更多:https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa