javagenericsclasscastexceptionatomicreference

Java AtomicReferenceFieldUpdater - ClassCastException when using generic types


I get the following error :

Exception in thread "main" java.lang.ClassCastException
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.<init>(AtomicReferenceFieldUpdater.java:336)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdater.java:109)
at org.telegram.bot.util.FieldTimer.<init>(FieldTimer.java:27)
at org.telegram.bot.util.FieldTimer.<init>(FieldTimer.java:19)
at org.telegram.bot.util.FieldTimer.main(FieldTimer.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

when using AtomicReferenceFieldUpdater for generic types.

I'am totally confused and I have no idea why I'm getting such an error.

the code I am using :

public final class FieldTimer<V> {

    private volatile V value;
    private final V defaultValue;
    private final long resetTimeout;
    private final AtomicLong lastSet;
    private final AtomicReferenceFieldUpdater<FieldTimer, V> updater;

    public FieldTimer(V value, Class<V> type, long resetTimeout, TimeUnit unit) {
        this(value, value, type, resetTimeout, unit);
    }

    public FieldTimer(V value, V defaultValue, Class<V> type, long resetTimeout, TimeUnit unit) {
        this.value = value;
        this.defaultValue = defaultValue;
        this.resetTimeout = unit.toMillis(resetTimeout);
        lastSet = new AtomicLong(System.currentTimeMillis());
        updater = AtomicReferenceFieldUpdater.newUpdater(FieldTimer.class, type, "value");
    }

    public V get() {
        return System.currentTimeMillis() - lastSet.get() >= resetTimeout
                ? defaultValue
                : value;
    }

    public void set(V value) {
        updater.set(this, value);
        lastSet.set(System.currentTimeMillis());
    }

    public boolean compareAndSet(V expect, V update) {
        boolean set = updater.compareAndSet(this, expect, update);
        if (set) {
            lastSet.set(System.currentTimeMillis());
        }
        return set;
    }

}

the exception occurs in this line of code :

updater = AtomicReferenceFieldUpdater.newUpdater(FieldTimer.class, type, "value");

but, if I change it to something like this :

public final class LongFieldTimer {

    private volatile Long value;

    ...

    public LongFieldTimer(Long value, Long defaultValue, long resetTimeout, TimeUnit unit) {
        ...
        updater = AtomicReferenceFieldUpdater.newUpdater(LongFieldTimer.class, Long.class, "value");
    }

}

then there would be no errors :| (but why???)

what causes this problem? and how to solve it?

Thanks.


Solution

  • Your field

    private volatile V value;
    

    is of type Object because of type erasure. The javadoc of AtomicReferenceFieldUpdater#newUpdater states that it throws

    ClassCastException - if the field is of the wrong type

    You've presumably passed Long.class or long.class as an argument for the type parameter. It expects Object.class.

    In your second example, the field is explicitly defined as being of type Long

    private volatile Long value;
    

    so Long.class will work, since that is the type of the field expected.