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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With