一、final在重载参数的可用性
之前在写代码的时候,在一个重载方法里由于用到里Runnable匿名类,需要对参数进行final指定,由于基类的参数都没有final修饰符,基类的函数原型如下:
public abstract void write(char[] buf, int offset, int count) throws IOException;
所以打算在内部重新声明final变量来赋值,即
@Override
public void write(char[] buf, int offset, int count) throws IOException {
final char[] writeBuf = buf;
}
不过尝试之下,发现可以直接在重载方法上添加final,即使基类的方法里的参数没有声明final,即
@Override
public void write(final char[] buf, int offset, int count) throws IOException {
}
编译可以通过。
二、具体原因
后来在stackoverflow查找相关原因的时候,发现以下解释:
According to the Java Language Specification 4.12.4:
Declaring a variable final can serve as useful documentation that its value will not change and can help avoid programming errors.
However, a final
modifier on a method parameter is not mentioned in the rules for matching signatures of overridden methods, and it has no effect on the caller, only within the body of an implementation.
具体来说,其实由于final修饰符只在当前实现函数中起作用,只是对编译器的一种提示,不会出现具体的.class文件中,而且无论参数是否final,java都只会进行pass by value的传参形式。说到底,final只是代表当前重载函数的具体实现,与接口无关,因此JSE规范里没有把这个列入函数签名中,属于具体实现细节。
三、其他可用修饰
这里稍微延伸一下,对于相同原理的还有synchronized关键字:
@Override
public synchronized void write(final char[] buf, int offset, int count) throws IOException {
}
即在重载函数里添加synchronized关键字同样是允许的,因为与final意义一样,是当前函数的具体实现,不影响接口声明。
至于对于声明里的异常抛出,则有不同规则,因为这会导致接口的实现的。
具体例子如下:
@Override
public synchronized void write(final char[] buf, int offset, int count) throws JSONException {
}
上面这个例子会导致编译失败,具体由于抛出里JSONException,比基类多抛出一个已经检查的异常。
@Override
public synchronized void write(final char[] buf, int offset, int count) {
}
有趣的是,上面这个例子编译可以通过,虽然比基类少声明里一个已经检查的异常。
不过这样做的确是合理的,因为实现接口的函数必然不能有比接口多的异常,否则会影响接口的使用。
另外还有对于修饰符不能低于接口的权限等等,也即接口是protected,实现函数就必须是protected以上的权限修饰。
四、详细资料
具体可以参考JSE的关于重载函数的要求:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.3
以及函数签名的解释:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.2