首页 > 代码库 > 【java解惑】java编译器对空final赋值的妥协

【java解惑】java编译器对空final赋值的妥协

    如下所示代码:

public class Example038 {

	private final static String GUEST_ID = "ape_it";
	private final static String ID;
	static {
		try {
			ID = getID();
		} catch (GetIDException e) {
			ID = GUEST_ID;
			e.printStackTrace();
		}
	}

	private static String getID() throws GetIDException {
		throw new GetIDException();
	}

	class GetIDException extends Exception {
	}
}


    代码分析:

    如上代码,在eclipse中进行编辑时会有编译错误提醒(ID域不能重复赋值)。ID域做为一个final域,只能赋值一次。从代码上看,ID确实也只是被赋值过一次,可是为什么会有如此提醒呢?

    要确定一个程序是否可以不止一次地对一个空 final 进行赋值是一个很困难的问题。 事实上,这是不可能的。这等价于经典的停机问题(类似于上帝悖论和理发师悖论),它通常被认为是不可能解决的。为了能够编写出一个编译器,语言规范在这一点上采用了保守的方式。在程序中,一个空 final 域只有在它是明确未赋过值的地方才可以被赋值。因为它是保守的,所以编译器必须拒绝某些可以证明是安全的程序。上述代码可以证明是安全的代码,但是编译器必须拒绝,因为它无法证明对空final域只赋值一次。

    解决上述由于编译器的妥协和保守而报告的错误的办法只能是修改代码,一种修改代码的方案是将空final域变为普通final域,如下所示:

public class Example038 {

	public final static String GUEST_ID = "ape_it";
	// private final static String ID;
	private final static String ID = getIDNew();

//	static {
//		try {
//			ID = getID();
//		} catch (GetIDException e) {
//			ID = GUEST_ID;
//                      e.printStackTrace();
//		}
//	}

	private static String getIDNew() {
		try {
			return getID();
		} catch (Exception e) {
			return GUEST_ID;
		}
	}

	private static String getID() throws GetIDException {
		throw new GetIDException();
	}

	static class GetIDException extends Exception {
		private static final long serialVersionUID = 1L;
	}
}



注:本【java解惑】系列均是博主阅读《java解惑》原书后将原书上的讲解和例子部分改编然后写成博文进行发布的。所有例子均亲自测试通过并共享在github上。通过这些例子激励自己惠及他人。同时本系列所有博文会同步发布在博主个人微信公众号搜索“爱题猿”或者“ape_it”方便大家阅读。如果文中有任何侵犯原作者权利的内容请及时告知博主以便及时删除如果读者对文中的内容有异议或者问题欢迎通过博客留言或者微信公众号留言等方式共同探讨。

源代码地址https://github.com/rocwinger/java-disabuse


本文出自 “winger” 博客,谢绝转载!

【java解惑】java编译器对空final赋值的妥协