Java中的SuppressWarnings("unchecked")是什么?

500

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

@SuppressWarnings("unchecked")

这是什么意思?

11个回答

461
有时候Java泛型并不能让你做想做的事情,那么你需要有效地告诉编译器,在执行时你所做的操作是合法的。通常我在模拟泛型接口时会觉得很痛苦,但也有其他例子。通常值得尝试找到避免警告的方法(这里可以参考 Java 泛型 FAQ),但有时即使可能也会扭曲代码,因此抑制警告更加整洁。在这种情况下,一定要添加解释性评论!同样的泛型 FAQ 中有几个关于此主题的部分,从 "什么是'未经检查'的警告?" 开始,值得一读。

13
有些情况下,你可以通过使用YourClazz.class.cast()来避免它。适用于单个泛型元素容器,但不适用于集合。 - akarnokd
或者更好的是使用通配符泛型(YourClazz<?>) - Java从不会对这些转换发出警告,因为它们是安全的。然而,这并不总是有效的(有关详细信息,请参见泛型常见问题解答)。 - Konrad Borowski

56

这是一种用于抑制编译警告的注释,适用于未检查的泛型操作(不包括异常),例如强制类型转换。它基本上意味着程序员不希望在编译特定代码时收到已经知道的警告信息。

关于此特定注释您可以在这里阅读更多:

SuppressWarnings

此外,Oracle在这里提供了有关注释使用的教程文档:

Annotations

正如他们所说,

“'unchecked'警告可能会在与早期代码(在《Java教程:泛型》中讨论)进行接口时发生。”


23

这也可能意味着当前的Java类型系统版本对你的情况不够好。有几个JSR提议/黑科技来修复此问题:类型令牌,超级类型令牌,Class.cast()。

如果你真的需要这种抑制,请尽量缩小范围(例如不要将其放在类本身或长方法上)。以下是一个示例:

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

   Collections.reverse(list);
   return list;
}

14

简单来说:这是编译器发出的警告,表示它无法确保类型安全。

例如,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


3
这是一个需要使用SuppressWarnings的情况的良好示例。 - Jimmy

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

这个转换是安全的,因为空的可选项中存储的值不能被检索,所以不存在意外的类转换异常风险。


11

SuppressWarning注解用于禁止编译器对注解元素的警告。具体来说,unchecked类别可以禁止由于未检查的类型转换而产生的编译器警告。


你的SupressWarning链接已经失效了,这里提供一个替代方案:http://docs.oracle.com/javase/specs/jls/se6/html/interfaces.html#9.6.1.5 - James Daily

6

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

示例:

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

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;

3
编译器无法确保类型安全,因此发出警告。 “unchecked”警告的名称有误导性。它并不意味着警告在任何方面都未经检查。术语“unchecked”指的是编译器和运行时系统没有足够的类型信息来执行所有必要的类型检查以确保类型安全。在这个意义上,某些操作是“unchecked”的。
“unchecked”警告最常见的来源是使用原始类型。当通过原始类型变量访问对象时,会发出“unchecked”警告,因为原始类型不提供足够的类型信息来执行所有必要的类型检查。
示例(与原始类型一起使用的未经检查的警告):
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 方法时,编译器不知道是否安全将一个字符串对象添加到集合中。如果 TreeSet 是包含字符串或其超类型的集合,则是安全的。但是从原始类型 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,而不是它是否为具有特定类型成员的wrapper。同样,对类型参数T的强制转换在运行时被转换为Object类型,并可能被完全优化掉。由于类型擦除,运行时系统无法在运行时执行更有用的类型检查。
在某种程度上,源代码是误导性的,因为它表明对相应目标类型的强制转换已经完成,而实际上强制转换的动态部分仅针对目标类型的类型擦除进行检查。 "unchecked"警告是为了引起程序员对强制转换的静态和动态方面之间不匹配的注意。请参考:什么是"unchecked"警告?

3
据我所知,目前它与抑制有关泛型警告有关; 泛型是一种新的编程结构,在 JDK 5 之前的版本中不受支持,因此旧结构与新结构的混合可能会导致一些意外的结果。编译器会向程序员发出警告,但如果程序员已经知道,他们可以使用 SuppressWarnings 关闭这些可怕的警告。

1
JDK5是新的吗?它已经完成了大部分的服务生命周期。 - Tom Hawtin - tackline
我知道JDK 5已经相当过时了,但我的意思是,它在引入Java中以前从未提供的新功能方面还是比较新的。此外,请注意,仍有一些人在等待JDK 7才会放弃JDK 5并采用JDK 6,我无法理解其中的原因! - BakerTheHacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接