在C#中,Java中的final相当于什么?

633

在C#中,与Java的final相当的关键字是readonly


150
置于类顶部的一条注释,写着“如果你重写这个类,你就被解雇啦!”(当然,这只是一个玩笑 :))。 - Hemant
7个回答

941

final关键字在Java中有几个用途。根据使用的上下文,它对应于C#中的sealedreadonly关键字。

为了防止子类化(从定义的类继承):

Java

public final class MyFinalClass {...}

C#

public sealed class MyFinalClass {...}

方法

防止覆盖一个 virtual 方法。

Java

public class MyClass
{
    public final void myFinalMethod() {...}
}

C#

public class MyClass : MyBaseClass
{
    public sealed override void MyFinalMethod() {...}
}

如Joachim Sauer所指出,这两种语言的一个显著区别是,Java默认将所有非静态方法标记为virtual,而C#将它们标记为sealed。因此,如果您想要停止进一步覆盖在基类中已明确标记为virtual的方法,只需在C#中使用sealed关键字即可。

变量

只允许变量被赋值一次:

Java

public final double pi = 3.14; // essentially a constant

C#

public readonly double pi = 3.14; // essentially a constant

作为一个旁注,readonly 关键字的效果与 const 关键字不同,因为 readonly 表达式是在运行时而不是编译时进行评估,因此允许任意表达式。

17
在Java中,所有非静态方法默认都是虚拟的。因此,虽然在C#中可以在初始定义中简单地省略virtual关键字,但在Java中需要使用"final"来避免子类覆盖它。 - Joachim Sauer
186
好的答案 - 在Java中,“final”的另一种用法是在本地变量或方法参数上使用,以防止重新分配。C#没有直接相当于这个用法。 - serg10
20
在构造函数中,readonly成员变量是可以被修改的:http://pastebin.com/AzqzYGiA - recursive
8
请注意:如果在Java中将成员变量声明为final,则编译器会发出警告,除非每个构造函数在每个代码路径上都分配一个值;而在C#中,只有在readonly成员变量的情况下才会发出警告。 - Mene
1
@NickolayKondratyev:是的,我的例子隐含了您需要从另一个类进行子类化。您实际上不需要接口;那是多余的,但除此之外,看起来差不多正确。 - Noldorin
显示剩余4条评论

216

47
对于最终的本地变量或方法参数,在C#中没有直接的等价物,这是一个重大区别。 - Daniel B. Chapman
3
如果你正在进行实例化,可以使用const来定义局部变量。这并不相同,因为当然final可以让你分别声明和初始化(从而拥有不同的值),但只是以防你不知道... - Griknok
6
const 只能用于值类型。据我所知,无法对本地引用类型创建有效的常量。 - jocull
2
@jocull 除了字符串之外。 - Raimund Krämer
2
Java有一些C#所没有的东西,其中一个是为了确保本地变量不会改变而使用final。我希望能够使用"final int someCount = someLinqEnumerable.Count();"来实现这个功能。(我正在展示我使用了一个方法来填充值,因此不能使用const int someCount) - granadaCoder
显示剩余2条评论

52

在这里所有人都忽略了Java对于final成员变量明确赋值的保证。

对于一个带有final成员变量V的类C,通过C的每一种可能的构造函数的每一个执行路径都必须恰好赋值给V一次——未赋值V或者在构造函数中赋值给V两次及以上会导致错误。

C#的readonly关键字没有这样的保证——编译器很愿意让readonly成员未被赋值或允许你在一个构造函数中多次为其赋值。

因此,final和readonly(至少在成员变量方面)绝对不是等价的——final更加严格。


9
正如提到的那样,sealed是方法和类的final等效语句。
至于其余部分,则较为复杂。
对于static final字段,static readonly是最接近的替代方案。它允许您在静态构造函数中初始化静态字段,这与Java中的静态初始化器非常相似。这适用于常量(基元和不可变对象)以及对可变对象的常量引用。 const修饰符对于常量来说非常相似,但您不能在静态构造函数中设置它们。
对于不应在离开构造函数后重新分配的字段,可以使用readonly。虽然它不等同于final——final要求在构造函数或初始化程序中仅有一个赋值。
据我所知,C#没有final本地变量的等效项。如果你想知道为什么会需要它:你可以在if-else、switch-case之前声明一个变量。通过将它声明为final,您强制它最多被赋值一次。
通常需要在读取变量之前至少分配一次Java本地变量。除非分支在值读取之前跳出,否则最终变量只分配一次。所有这些都在编译时进行检查。这需要行为良好的代码,并减少了错误的余地。
总的来说,C# 没有直接等价于 final 的功能。虽然 Java 缺少一些 C# 的好用特性,但作为一个主要使用 Java 的程序员,看到 C# 无法提供相应的功能还是让我感到振奋。

我记得使用最终的局部变量来解开一些混乱的代码。当我认为一个变量在方法中不会被覆盖时,我只是去掉了对null的初始化,添加了final关键字。如果编译通过,那么我就使它更清晰了。如果不能通过编译,那么我就学到了有关它被重新赋值的有用信息。然后,我可以提出更难的问题,比如:这是否是预期的行为?如果是,我如何澄清这一点呢? - Vlasec

6

Java类中的final关键字和方法中的final关键字 -> 被称为sealed。 Java成员变量中的final关键字 -> 运行时常量使用readonly,编译时常量使用const。

局部变量的final关键字和方法参数的final关键字没有等效的关键字。


5

0

密封的


14
只回答一部分并不完整,因为它取决于上下文,添加解释和/或示例将使需要帮助的人更易理解。 - Rune FS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接