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

Posted

技术标签:

【中文标题】Java 中的 SuppressWarnings(“未选中”)是啥?【英文标题】:What is SuppressWarnings ("unchecked") in Java?Java 中的 SuppressWarnings(“未选中”)是什么? 【发布时间】:2009-07-15 06:50:58 【问题描述】:

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

@SuppressWarnings("unchecked")

这是什么意思?

【问题讨论】:

【参考方案1】:

有时 Java 泛型并不能让你做你想做的事,你需要有效地告诉编译器你正在做的事情真正在执行时是合法的。

当我模拟一个通用接口时,我通常会觉得这很痛苦,但也有其他例子。通常值得尝试找出一种避免警告而不是抑制警告的方法(Java Generics FAQ 在这里有所帮助)但有时即使它可能的,它也会使代码变形太多以至于抑制警告更整洁。在这种情况下,请务必添加解释性评论!

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

【讨论】:

在某些情况下,您可以使用 YourClazz.class.cast() 来避免它。适用于单个通用元素容器,但不适用于集合。 或者最好使用通配符泛型(YourClazz<?>) - Java 从不警告此类转换,因为它们是安全的。但是,这并不总是有效(有关详细信息,请参阅泛型常见问题解答)。【参考方案2】:

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

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

SuppressWarnings

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

Annotations

正如他们所说,

“与泛型出现之前编写的遗留代码交互时,可能会出现‘未检查’警告(在名为泛型的课程中讨论过)。”

【讨论】:

【参考方案3】:

这也可能意味着当前的 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;

【讨论】:

【参考方案4】:

简单地说:这是一个警告,编译器表明它不能确保类型安全。

以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

【讨论】:

这是我们需要使用 SuppressWarnings 情况的一个很好的例子。【参考方案5】:

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

【讨论】:

您的 SupressWarning 链接已失效;这是另一种选择:docs.oracle.com/javase/specs/jls/se6/html/…【参考方案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);

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

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

【讨论】:

【参考方案7】:

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

例子:

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

【讨论】:

【参考方案8】:

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

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;

【讨论】:

【参考方案9】:

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

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

【讨论】:

JDK5 是新的?它已经完成了大部分使用寿命。 我知道 JDK 5 已经过时了,我的意思是,它是新的,因为它为 Java 引入了以前未在 JDK 4 中提供的新功能。另请注意,那些仍然存在为了拥抱 JDK 6,在离开 JDK 5 之前等待 JDK 7,我无法解释为什么!【参考方案10】:

编译器指示它不能确保类型安全的警告。 术语“未经检查的”警告具有误导性。这并不意味着以任何方式未检查警告。术语“未检查”是指编译器和运行时系统没有足够的类型信息来执行确保类型安全所必需的所有类型检查。从这个意义上说,某些操作是“未经检查的”。

“未经检查”警告的最常见来源是使用原始类型。当通过原始类型变量访问对象时会发出“未检查”警告,因为原始类型没有提供足够的类型信息来执行所有必要的类型检查。

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

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 (或其超类型)的集合,那么它将是安全的。但是从原始类型 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 类型,并且可能完全优化掉了。由于类型擦除,运行时系统无法在运行时执行更有用的类型检查。

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

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

【讨论】:

【参考方案11】:

@SuppressWarnings 注解是 JDK 中可用的三个内置注解之一,在 Java 1.5 中与 @Override 和 @Deprecated 一起添加。

@SuppressWarnings 指示编译器忽略或抑制注释元素中指定的编译器警告以及该元素内的所有程序元素。 例如,如果一个类被注释为抑制特定警告,那么该类内部方法中生成的警告也将被分离。

您可能已经看过@SuppressWarnings("unchecked") 和@SuppressWarnings("serial"),这是@SuppressWarnings 注释的两个最流行的例子。前者用于抑制由于未经检查的强制转换而产生的警告,而后者用于提醒在 Serializable 类中添加 SerialVersionUID。

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

【讨论】:

以上是关于Java 中的 SuppressWarnings(“未选中”)是啥?的主要内容,如果未能解决你的问题,请参考以下文章

@suppressWarnings("unchecked")在java中的作用

Java中@SuppressWarnings注解用法(转)

请教Java SE高手一个简单的“@SuppressWarnings("unlikely-arg-type")”注释的含义是啥?

Java中@SuppressWarnings注解可以使用的参数列表

java注解 @SuppressWarnings注解用法

@SuppressWarnings是啥意思