Answers:
有时Java泛型只是没有让你做你想要什么,你需要有效地告诉编译器,你在做什么真的会是在执行时的法律。
当我模拟通用接口时,通常会感到很痛苦,但也有其他示例。通常值得尝试的是找到一种避免警告而不是抑制警告的方法(Java Generics FAQ在这里有所帮助),但是有时即使有可能,它也会使代码变形,以至于抑制警告变得更加整洁。在这种情况下,请务必添加解释性注释!
相同的泛型FAQ在此主题上有多个部分,以“什么是“未经检查”的警告?”开头。-值得一读。
(YourClazz<?>)
-Java从不警告此类强制转换,因为它们是安全的。但是,这并不总是有效(有关详细信息,请参见泛型FAQ)。
这也可能意味着当前的Java类型系统版本不足以满足您的情况。有几个解决这个问题的JSR命题 / hacks:类型令牌,超级类型令牌,Class.cast()。
如果您确实需要此抑制,请尽可能缩小它的范围(例如,不要将其放在类本身或长方法上)。一个例子:
public List<String> getALegacyListReversed() {
@SuppressWarnings("unchecked") List<String> list =
(List<String>)legacyLibrary.getStringList();
Collections.reverse(list);
return list;
}
该SuppressWarning注释用于抑制编译器警告的注解元素。具体来说,该unchecked
类别允许抑制由于未经检查的类型转换而生成的编译器警告。
简单:这是一个警告,编译器通过该警告表明它无法确保类型安全。
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上
在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);
ClassCastException
随着Java编译器插入隐式强制转换,JVM将抛出意外情况。
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;
}
这种转换是安全的,因为无法检索存储在空的Optional中的值,因此不存在意外的类转换异常的风险。
您可以禁止编译器警告,并告诉泛型您根据其编写的代码是合法的。
例:
@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(或其超类型)的集合,那么它是安全的。但是根据原始类型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的警告。
了解更多:https://javarevisited.blogspot.com/2015/09/what-is-suppresswarnings-annotation-in-java-unchecked-raw-serial.html#ixzz5rqQaOLUa