接口方法中的最终参数-有什么意义?


189

在Java中,final在接口方法中定义参数并且在实现类中不遵循参数是完全合法的,例如:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

在上面的示例中,bar与VS类baz具有相反final定义的接口。

以相同的方式,final当一个类方法扩展另一个(无论是否扩展)时,不会强制执行任何限制abstract

尽管final在类方法主体内部有一些实用价值,但final接口方法参数是否有任何规定?


final由于本机类型已被复制,因此无论如何都不会对其进行任何处理。
Paul Tomblin

11
只是作为讨论的要点:我刚刚尝试过,如果两个interface定义仅在final参数的属性上有所不同,则生成的.class文件逐字节相同(当然javap -v会产生相同的输出)。final顺便说一句,对于两个仅在属性上有所不同的类,也是如此!
Joachim Sauer

2
@Paul:它与引用类型具有完全相同的作用:它防止参数本身被修改(如果在实现中使用)。
Joachim Sauer

在方法签名中,它与公众一样重要。
罗宾

2
@Deepak:我看到您正在就各种问题要求工作示例,即使这似乎没有多大意义。我认为您应该尝试学习一些抽象的思想:尝试在不考虑任何可执行代码的情况下思考问题。从长远来看,它将对您有很大帮助。
约阿希姆·绍尔

Answers:


103

似乎没有任何意义。根据Java语言规范4.12.4

声明变量final可作为有用的文档,其值不会改变,并有助于避免编程错误。

但是,final用于匹配覆盖的方法的签名规则中未提及方法参数上的修饰符,并且修饰符仅在实现主体内对调用方没有影响。同样,正如罗宾在评论中指出的那样,final方法参数上的修饰符对生成的字节码没有影响。(对于的其他用途,则不是这样final。)


方法参数也可以作为变量吗?它显然是在实践中,但是是否在规范上下文中?
mindas 2011年

11
它不能用于匹配签名,因为它没有出现在实际的.class文件中。它仅适用于编译器。
罗宾

@mindas-JLS说有七种变量。方法参数在列表中排在第四位。
泰德·霍普

3
但是,文档几乎是无用的,因为final没有在实现类上强制使用修饰符。您的界面签名可能只是在撒谎。
Stefan Haberl

Java 8语言规范现在说,有八种变量(从七种开始-它们添加了lambda参数)。方法参数仍排在列表的第四位(至少生活中的某些事情看起来很稳定。
特德·霍普

25

在子类中插入实现方法时,某些IDE会复制abstract / interface方法的签名。

我认为这对编译器没有任何影响。

编辑:虽然我相信这在过去是对的,但我认为当前的IDE不再这样做。


2
有效的观点,尽管我认为实现此功能(或无意中离开)时并没有很多IDE :-)
mindas 2011年

2
我认为是static transient领域类别。;)
Peter Lawrey 2011年

1
与公共构造函数有关的抽象类。
彼得·劳瑞

1
IntelliJ做到了。我的版本是IntelliJ IDEA 2016.1.3 Build#IU-145.1617。
睡鼠

1
@Dormouse,这很有趣,因为Android Studio(3.1.4 Build#AI-173.4907809)不:(
The Godfather

18

方法参数的最终注释始终仅与方法实现相关,而与调用方无关。因此,没有真正的理由在接口方法签名中使用它们。除非您要在所有方法签名中遵循相同的一致编码标准,该标准要求最终的方法参数。这样就很好了。


6

更新:下面的原始答案是在没有完全理解该问题的情况下编写的,因此不能直接解决该问题。:)尽管如此,对于希望了解final关键字的一般用法的人员来说,它必须是有益的。

关于这个问题,我想从下面引用我自己的评论。

我相信您不会被迫实施某个论点的终结性,让您自由决定是否应该 最终在自己的实现与否。

但是,是的,您可以final在接口中声明它却拥有它,这听起来很奇怪在实现中将其定为最终值。如果满足以下条件,那将更有意义:

一个。 final接口(抽象)方法参数不允许使用关键字(但是您可以在实现中使用它),或者
b。final在接口中声明参数将强制final在实现中声明该参数(但对于非最终声明则不强制)​​。


我可以想到方法签名可以具有final参数的两个原因:BeanObjects实际上,它们都是相同的原因,但是上下文略有不同。

对象:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

final关键字确保我们不会意外地创建一个新的地方通过展示编译错误炒菜锅当我们试图这样做。这样可以确保将鸡汤添加到该addChicken方法获得的原始烹饪锅中。与此相比addVegetables,我们损失了花椰菜的地方是因为花椰菜将它添加到了新的本地烹饪锅中,而不是原来的锅中。

Beans: 与对象的概念相同(如上所示)。Bean本质上Object是Java中的。但是,bean(JavaBeans)在各种应用程序中用作存储和传递定义的相关数据集合的便捷方法。正如addVegetables可能通过创建一个新的烹饪锅StringBuilder并将其与花椰菜一起扔掉而使烹饪过程混乱一样,它也可以通过烹饪锅JavaBean来做同样的事情。


好。这并不是一个正确的答案(因为我没有被迫使接口方法的实现接受最终参数,即使接口是这样),但它仍然很有帮助,因为它很好地解释了为什么首先确定方法参数为一个好主意。
2014年

我相信你不会被迫实行论证的终结离开你自由决定是否应该最终在自己的实现与否。但是,是的,您可以final在接口中声明它,但在实现中将其定为最终值,这听起来很奇怪。如果(a) final不允许接口(抽象)方法参数使用关键字(但您可以在实现中使用它),或者(b)final在接口中声明一个参数将迫使它final在实现中进行声明,则更有意义。 (但不强制非决赛)。
ADTC

“这不是一个正确的答案”“是的,我不知道我在想什么……一定是在七月的凌晨。:)
ADTC

2

我相信这可能是一个多余的细节,因为它是否最终是实现细节。

(排序方式类似于将接口中的方法/成员声明为公共。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.