简体   繁体   English

在Java中强制转换为泛型类时键入安全警告

[英]Type Safety warning when casting to generic class in Java

I have a class 我有一堂课

class Property<T> {
    value T;

    public void setValue(T value) {
        this.value = value;
    }
}

Now I have a list List<Property<?>> properties with various properties of various Types. 现在,我有了一个列表List<Property<?>> properties其中包含各种类型的各种属性。 I loop through this list and want to set the value of every Property. 我遍历此列表,并希望设置每个属性的值。 Therefore I need to cast the generic Property to the correct type like: eg for Integer: 因此,我需要将通用属性转换为正确的类型,例如:对于Integer:

if (property.getType().equals("Integer")) {
        Property<Integer> propInt = (Property<Integer>) property;
        propInt.setValue((Integer) values[i++]);
}

where values[] is an Object array which holds the values that I want to set. 其中values[]是一个对象数组,其中包含我要设置的值。

Everything works fine, but Java complains about Type Safety "Unchecked cast from Property<?> to Property<Integer> ". 一切正常,但是Java抱怨类型安全“未经检查从Property<?>Property<Integer> ”。 A check like 像这样的支票

if (property instanceof Property<Integer>){...}

is not possible though. 虽然不可能。 How can I change my code to get rid of this warning, or do you know a better practice for my case? 我该如何更改代码以消除此警告,或者您知道我的案例的更好做法?

The compiler complains because property is of type Property<?> which could or could not be of type Property<Integer> in general. 编译器抱怨是因为property的类型为Property<?> ,通常它可能是也不可能是Property<Integer>类型。 This is an inherent limitation of the current java language due to type erasure. 由于类型擦除,这是当前Java语言的固有限制。

In this particular case you are making sure that property is of class Property<Integer> by use of the getType method so it is safe to ignore the warning. 在这种特殊情况下,您可以通过使用getType方法来确保该property属于Property<Integer>类,因此可以安全地忽略该警告。

if (property.getType().equals("Integer")) {
        // we have made sure property is of class Property<Integer> so the cast is type safe
        @SuppressWarnings("unchecked")
        Property<Integer> propInt = (Property<Integer>) property;
        propInt.setValue((Integer) values[i++]);
}

It is important to document it with a comment otherwise a peer reviewing your code might not notice that indeed the cast is type safe and could confuse the warning supression as a malpractice. 重要的是要用注释记录下来,否则查看您的代码的同行可能不会注意到确实类型转换是安全的,并且可能将警告抑制作为不当行为加以混淆。

The warning indicates, that at source level, the list may contain objects of any type () and you cast those to Integer, which would lead to a ClassCastException at runtime, in cases where the list contains a non-Integer object. 警告表明,在源级别,列表可能包含任何类型()的对象,并且将它们强制转换为Integer,如果列表包含非Integer对象,则在运行时会导致ClassCastException。 So the warning indicates a potential problem at runtime. 因此,警告表明在运行时存在潜在问题。

You could get rid of the warning by either 您可以通过以下两种方法摆脱警告

  • declaring the List as List of Integers List<Integer> 将列表声明为整数List<Integer>
  • or, suppress the warning using the @SuppressWarnings("unchecked") on the method or the list declaration. 或者,使用方法或列表声明中的@SuppressWarnings("unchecked")警告。

If you can not guarantee, that the objects contained in the list are no Integer, then you should have the instanceof before the cast and the SuppressWarning. 如果不能保证列表中包含的对象不是Integer,则在转换和SuppressWarning之前应该具有instanceof If you can guarantee it, then you should declare the list as list of Integers. 如果可以保证,则应将该列表声明为Integers列表。

You can use annotation SuppressWarnings to ignore this warning: 您可以使用annotation SuppressWarnings忽略此警告:

@SuppressWarnings("unchecked") Property<Integer> propInt = (Property<Integer>) property;

but it's a risk to do this. 但是这样做是有风险的。

this warning is caused by type erasure , because when compile, generic type will be erased. 此警告是由于类型擦除引起的,因为在编译时, 泛型将被擦除。

As you know generics in java suffer a bit from type erasure, all becomes Object at run-time. 如您所知,java中的泛型会遭受类型擦除,所有这些在运行时都变为Object。

As you test on strings like "Integer", it would seem adding a Class<T> could even be convenient. 当您对“ Integer”之Class<T>字符串进行测试时,添加Class<T>似乎更为方便。

class Property<T> {
    final Class<T> type;
    value T;

    public Property(Class<T> type) {
        this.type = type;
    }

    public void setAnyValue(Object value) {
        this.value = type.cast(value);
    }
}

Property<?> property = ...
//if (property.getType() == Integer.class) {
//    Property<Integer> propInt = (Property<Integer>) property;
//    propInt.setAnyValue((Integer) values[i++]);
//}
property.setAnyValue(values[i++]);

The usage of Object is due to the polymorph usage of Property instead of the more normal typing: your code mixes up differently typed properties and parallel values. Object的使用归因于Property的多态使用,而不是更普通的键入:您的代码混合了不同类型的属性和并行值。

If you are using the spring framework, you can also use: 如果您正在使用spring框架,则还可以使用:

import static org.springframework.data.util.CastUtils.cast;
        Map<String,Object> messageMap = cast(exchange.getMessage().getBody());
        List<String> refIds = cast(messageMap.get("refIds"));

Note: 注意:

I wouldn't suggest using this unless the spring framework is already part of your application. 除非spring框架已成为您应用程序的一部分,否则我不建议您使用此功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM