简体   繁体   中英

How can I use Java Generic to suppress the warning?

In the code below,
a warning appears while assigning a value to a generic type field in the update method of the Device class.

How do I fix it to get the correct code?

    @Getter
    @Setter
    public class Device<T> {
        private boolean updated;
        private T value;
        private Date updatedAt;

        public void update(Device<?> p) {
            this.value = (T) p.getValue(); // unchecked case warning
            this.updatedAt = p.getUpdatedAt();
            this.updated = true;
        }
    }
    Map<DeviceName, Device<?>> currDevices= getDevices(...);
    Map<DeviceName, Device<?>> newDevices = getNewDevices(...);

    for (DeviceName name : currDevices.keySet()) {
        Device<?> device = currDevices.get(name);
        Device<?> newDevice = newDevices.get(name);

        if (device.getUpdatedAt() == null || device.getUpdatedAt().before(newDevice.getUpdatedAt())) {
            // If I modify the device.update method -> public void update(Device<T> p) {
            // An error occurs here.
            device.update(newDevice);
        }
    }

When you write (T) foo , where T is a generics type variable, you're making a type assertion . As in, that is a literal no-op. That cannot ever throw a ClassCastException and doesn't convert anything whatsoever. Nevertheless, it may not be a T (you're simply telling the compiler: I'm sure it is, based on nothing other than because I say so), which means someplace else, much later and perhaps a different thread, a ClassCastException occurs even though you're not casting anything.

The solution is generally to never use the cast operator when any type var is in the parens.

Here, that's a simple matter: Make that Device<? extends T> p Device<? extends T> p instead of Device<?> p , easy peasy. It makes sense, presumably: A Device<Number> cannot be updated with a Device<String> ; you'd need a Device<Number> or perhaps a Device<Integer> .

Map<DeviceName, Device<?>> currDevices= getDevices(...);

This is a so-called heterogenous map; a map that maps DeviceName to many different types. Java doesn't like these, and generally that means you're now committed to these warnings. You cannot get rid of them without rewriting how this code works entirely. That map needs to go away somehow. Java has absolutely no way to verify that a currDevices.get("foo") returns the same kind of Device as newDevices.get("foo") .

There are workarounds but nothing universal. You'd have to explain far more detail what Device is for, what the T represents, what update is supposed to do, how your system works, etcetera.

Nevertheless, if you're going to go with this setup where a cast-with-warning is inevitable, at least put the error in the right place, which is not by making the signature of update() broken, as that's a method signature. Those are important.

Instead, then 'fix' it at call site. Here that would mean making device (the local variable) a raw type (just Device` - yes, that gets you a warning, that's the point: You can't avoid it without rewriting to not use a heterogenous map). That way the generics issues at least do not show up in method signatures.

If we can't eliminate the “unchecked cast” warning and we're sure that the code provoking the warning is typesafe, we can suppress the warning using the SuppressWarnings(“unchecked”) annotation.

@Getter
@Setter
public class Device<T> {
    private boolean updated;
    private T value;
    private Date updatedAt;

    public void update(Device<?> p) {
        SuppressWarnings(“unchecked”) 
        this.value = (T) p.getValue(); // unchecked case warning
        this.updatedAt = p.getUpdatedAt();
        this.updated = true;
    }
}

The compiler has to allow this cast to preserve backward compatibility with older Java versions that do not support generics. ClassCastException won't be thrown until we load the data with the wrong type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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