什么';Java处理闭包的方式是什么?
我熟悉函数语言和闭包,并对以下错误感到惊讶: 这是我的密码:什么';Java处理闭包的方式是什么?,java,Java,我熟悉函数语言和闭包,并对以下错误感到惊讶: 这是我的密码: Session dbSession = HibernateUtil.getSessionFactory().openSession(); Transaction dbTransaction = dbSession.beginTransaction(); Criteria criteria = dbSession.createCriteria(Invite.class).add(Restrictions.eq("uuid", path)
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);
}
据我所知,在
TimeTask
实例中引用invite
是一个错误,因为该变量不能保证存在。所以我的问题是,Java是如何表达我想要的,即加载一个invite,然后设置一个计时器,使invite在一段时间后过期。据我所知,错误并不在于不能保证invite不存在。错误应为:
"cannot refer to a non-final variable inside an inner class defined in a different method"
我认为错误是因为当invite
变量不能保证这样做时,它会导致各种各样的问题
如果Java运行时输入以下代码:
new TimerTask() {
@Override
public void run() {
// ERROR: `invite` is not guaranteed to exist when this code runs
invite.setExpired(true);
}
}
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.
}
它将invite
的值(参考)复制到新的TimerTask
对象。它不会引用变量本身。在方法被保留之后,在所有变量都不存在之后(它从调用堆栈中回收)。如果引用变量,可以创建一个悬空指针
我认为Java希望变量是final
,因为以下代码:
new TimerTask() {
@Override
public void run() {
// ERROR: `invite` is not guaranteed to exist when this code runs
invite.setExpired(true);
}
}
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.
}
您可能希望稍后在流程中将invite
设置为null
。这将对TimerTask
对象产生影响。为了避免此类问题,通过强制变量为final
,可以清楚地知道传递给TimerTask
的值是什么,以后不能修改,Java程序员只需认为方法调用的值“始终存在”这要容易得多。这里似乎有一些更基本的数据库设计和软件架构问题
与其在一定时间后设置一些“过期”字段,不如存储实际的过期时间。然后,当用户执行操作时,只需对照当前时间检查过期时间,以查看邀请是否过期。这样,它总是有效的,您不必安排计时器或管理长时间运行的事务或诸如此类的事情。它还隐式地在程序重启/崩溃时保持不变(如果程序在计时器运行时终止,当前基于计时器的方法将需要努力防止它忘记终止挂起的邀请)
如果您希望实时通知到期情况,请将“用户已收到通知”字段(例如)添加到邀请中。然后创建一个重复的后台任务(一个计时器可以完成此任务,或者一个ScheduledExecutorService
),该任务在单个条件查询中定期获取所有过期未通知邀请的列表。发出通知,设置通知标志,冲洗,重复。如果通知非常耗时(例如发送电子邮件),您可以在线程池ExecutorService
中排队通知(如果愿意)
闭包(或其近似值)不是这个工作的合适工具
但是,如果必须执行定时标志,请将hibernate设置为每线程会话对象模式(实际上,我认为这甚至可能是默认模式),然后使用线程池ExecutorService(请参阅Executors)来调度打开事务、查询邀请、等待(无计时器)的任务,然后执行该任务并关闭事务。然后,您的整个事务都在一个后台线程上,您遇到的奇怪的事务管理问题不再存在
更好的是,停止在一个长时间运行的事务中尝试所有这些(例如,如果用户希望在计时器运行时删除邀请,该怎么办?)。打开事务,然后查询邀请,然后关闭它。然后设置计时器(或使用ScheduledExecutorService
),让计时器打开事务,查询邀请,使邀请过期,然后关闭它。您可能不想在整个时间间隔内保持数据库连接和/或事务打开,没有理由这样做
至于最后一件事,非最终变量不能在匿名内部类中引用,因为您不能总是保证它们的值在匿名类代码运行之前不会更改(编译器不会,通常也不会,费尽心思分析匿名类是如何用来做保证的)。因此,它需要
final
只需宣布邀请最终:
final Invite invite = ...;
您可以在匿名类中使用它
可以找到引擎盖下的解释提示
是的,您仍然可以修改invite
的字段。您无法将invite分配给新对象。但就像我说的,你的方法很古怪,所以你遇到了问题
我正在打电话,或者我会从JLS的相关部分找到最后的资料。您可以在那里查找更多信息。有两种方法可以解决此问题:
invite
声明为final
,以便匿名内部类可以访问它
final Invite invite = (Invite) criteria.uniqueResult();
...
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
invite.setExpired(true);
}
}, MAX_TIME);
public class InviteTimeoutTask extends TimerTask {
private final Invite invite;
public InviteTimeoutTask(Invite invite) {
this.invite = invite;
}
@Override
public void run() {
invite.setExpired(true);
}
}
然后像这样使用它:
final Invite invite = (Invite) criteria.uniqueResult();
...
Timer timer = new Timer();
timer.schedule(new InviteTimeoutTask(invite), MAX_TIME);
您只能在匿名内部类中引用
final
变量的原因很简单,因为您处理的是局部变量。如果你在一个领域尝试同样的方法,你不会遇到任何问题。但局部变量的范围仅限于它所属的方法。当调用TimerTask
中的回调方法时,创建TimerTask
的方法已经很长时间了,所有局部变量都消失了。但是,如果将变量声明为final
,编译器可以在匿名类中安全地使用它 尝试final Invite=(Invite)条件。uniqueResult()代码>?只需升级到Java 8或那样做^。我认为这只是检测到Java程序员可能存在的一些问题/错误想法@