Java AtomicReferenceFieldUpdater - ClassCastException when using generic types

507 Views Asked by At

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.

1

There are 1 best solutions below

0
On BEST ANSWER

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.