Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the Java way of handling closures?

Tags:

java

I am comfortable with functional languages and closures and was surprised by the following error: "Cannot refer to the non-final local variable invite defined in an enclosing scope".

Here is my code:

Session dbSession = HibernateUtil.getSessionFactory().openSession();
Transaction dbTransaction = dbSession.beginTransaction();
Criteria criteria = dbSession.createCriteria(Invite.class).add(Restrictions.eq("uuid", path).ignoreCase());
Invite invite = (Invite) criteria.uniqueResult();
if (invite.isExpired()) {
    // Notify user the invite has expired.
} else {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            // ERROR: `invite` is not guaranteed to exist when this code runs
            invite.setExpired(true);
        }
    }, MAX_TIME);
}

As I understand it, referencing invite in the TimeTask instance is an error because that variable is not guaranteed to exist. So my question is, what is the Java way of expressing what I want, namely to load an invite and then set a timer to expire the invite after some amount of time.

like image 215
jds Avatar asked May 27 '15 03:05

jds


1 Answers

As far as I know, the error is not that it is not guaranteed that invite does not exists. The error should read:

"cannot refer to a non-final variable inside an inner class defined in a different method"

I think the error is because it will cause all kinds of trouble when the invite variable is not guaranteed to do so.

If the Java runtime enters the following code:

new TimerTask() {
    @Override
    public void run() {
        // ERROR: `invite` is not guaranteed to exist when this code runs
        invite.setExpired(true);
    }
}

It will copy the value (reference) of invite to the new TimerTask object. It will not refer to the variable itself. After the method is left, after all the variable no longer exists (it is recycled from the call stack). If one would refer to the variable, one could create a dangling pointer.

I think Java wants the variable to be final because of the following code:

Session dbSession = HibernateUtil.getSessionFactory().openSession();
Transaction dbTransaction = dbSession.beginTransaction();
Criteria criteria = dbSession.createCriteria(Invite.class).add(Restrictions.eq("uuid", path).ignoreCase());
Invite invite = (Invite) criteria.uniqueResult();
if (invite.isExpired()) {
    // Notify user the invite has expired.
} else {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            // ERROR: `invite` is not guaranteed to exist when this code runs
            invite.setExpired(true);
        }
    }, MAX_TIME);
    invite = null; //after creating the object, set the invite.
}

One could want to set invite later in the process to null. This would have implications on the TimerTask object. In order avoid that kind of problems, by enforcing the variable to be final, it is clear what value is passed to the TimerTask it cannot be modified afterwards and a Java programmer only has to think that the values for a method call "always exist" which is far easier.

like image 192
Willem Van Onsem Avatar answered Nov 14 '22 23:11

Willem Van Onsem