Java 在构造函数或声明中初始化类字段?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24551/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:15:59  来源:igfitidea点击:

Initialize class fields in constructor or at declaration?

java

提问by mmcdole

I've been programming in C# and Java recently and I am curious where the best place is to initialize my class fields.

我最近一直在用 C# 和 Java 编程,我很好奇初始化我的类字段的最佳位置。

Should I do it at declaration?:

我应该在声明时这样做吗?:

public class Dice
{
    private int topFace = 1;
    private Random myRand = new Random();

    public void Roll()
    {
       // ......
    }
}

or in a constructor?:

还是在构造函数中?:

public class Dice
{
    private int topFace;
    private Random myRand;

    public Dice()
    {
        topFace = 1;
        myRand = new Random();
    }

    public void Roll()
    {
        // .....
    }
}

I'm really curious what some of you veterans think is the best practice. I want to be consistent and stick to one approach.

我真的很好奇你们中的一些退伍军人认为最好的做法是什么。我想保持一致并坚持一种方法。

采纳答案by kokos

My rules:

我的规则:

  1. Don't initialize with the default values in declaration (null, false, 0, 0.0…).
  2. Prefer initialization in declaration if you don't have a constructor parameter that changes the value of the field.
  3. If the value of the field changes because of a constructor parameter put the initialization in the constructors.
  4. Be consistent in your practice (the most important rule).
  1. 不要使用声明中的默认值 ( null, false, 0, 0.0...) 进行初始化。
  2. 如果您没有更改字段值的构造函数参数,则首选声明中的初始化。
  3. 如果字段的值因构造函数参数而改变,则将初始化放在构造函数中。
  4. 在你的实践中保持一致(最重要的规则)。

回答by Noel

Assuming the type in your example, definitely prefer to initialize fields in the constructor. The exceptional cases are:

假设您的示例中的类型,绝对更喜欢在构造函数中初始化字段。例外情况是:

  • Fields in static classes/methods
  • Fields typed as static/final/et al
  • 静态类/方法中的字段
  • 字段类型为 static/final/et al

I always think of the field listing at the top of a class as the table of contents (what is contained herein, not how it is used), and the constructor as the introduction. Methods of course are chapters.

我总是认为类顶部的字段列表是目录(这里包含什么,而不是如何使用),而构造函数是介绍。方法当然是章节。

回答by Dan Blair

What if I told you, it depends?

如果我告诉你,这要看情况吗?

I in general initialize everything and do it in a consistent way. Yes it's overly explicit but it's also a little easier to maintain.

我通常会初始化所有内容并以一致的方式进行。是的,它过于明确,但它也更容易维护。

If we are worried about performance, well then I initialize only what has to be done and place it in the areas it gives the most bang for the buck.

如果我们担心性能,那么我只初始化必须完成的工作并将其放在最划算的区域。

In a real time system, I question if I even need the variable or constant at all.

在实时系统中,我怀疑我是否甚至需要变量或常量。

And in C++ I often do next to no initialization in either place and move it into an Init() function. Why? Well, in C++ if you're initializing something that can throw an exception during object construction you open yourself to memory leaks.

而在 C++ 中,我经常在任何地方都没有初始化并将其移动到 Init() 函数中。为什么?好吧,在 C++ 中,如果您正在初始化可能在对象构造过程中抛出异常的内容,您将面临内存泄漏。

回答by Iker Jimenez

I normally try the constructor to do nothing but getting the dependencies and initializing the related instance members with them. This will make you life easier if you want to unit test your classes.

我通常尝试构造函数除了获取依赖项并用它们初始化相关的实例成员之外什么都不做。如果你想对你的类进行单元测试,这会让你的生活更轻松。

If the value you are going to assign to an instance variable does not get influenced by any of the parameters you are going to pass to you constructor then assign it at declaration time.

如果您要分配给实例变量的值不受您要传递给构造函数的任何参数的影响,则在声明时分配它。

回答by Quibblesome

In C# it doesn't matter. The two code samples you give are utterly equivalent. In the first example the C# compiler (or is it the CLR?) will construct an empty constructor and initialise the variables as if they were in the constructor (there's a slight nuance to this that Jon Skeet explains in the comments below). If there is already a constructor then any initialisation "above" will be moved into the top of it.

在 C# 中没有关系。您提供的两个代码示例完全等效。在第一个示例中,C# 编译器(或者是 CLR?)将构造一个空的构造函数并初始化变量,就像它们在构造函数中一样(Jon Skeet 在下面的评论中解释了这一点的细微差别)。如果已经有一个构造函数,那么任何“上面”的初始化都将被移到它的顶部。

In terms of best practice the former is less error prone than the latter as someone could easily add another constructor and forget to chain it.

就最佳实践而言,前者比后者更不容易出错,因为有人可以轻松添加另一个构造函数而忘记链接它。

回答by John Meagher

There is a slight performance benefit to setting the value in the declaration. If you set it in the constructor it is actually being set twice (first to the default value, then reset in the ctor).

在声明中设置值有轻微的性能优势。如果你在构造函数中设置它,它实际上被设置了两次(首先是默认值,然后在 ctor 中重置)。

回答by Tom Hawtin - tackline

The semantics of C# differs slightly from Java here. In C# assignment in declaration is performed before calling the superclass constructor. In Java it is done immediately after which allows 'this' to be used (particularly useful for anonymous inner classes), and means that the semantics of the two forms really do match.

C# 的语义在这里与 Java 略有不同。在 C# 中,声明中的赋值是在调用超类构造函数之前执行的。在 Java 中,它立即完成,然后允许使用“this”(对于匿名内部类特别有用),这意味着这两种形式的语义确实匹配。

If you can, make the fields final.

如果可以,请将字段设为最终字段。

回答by xji

I think there is one caveat. I once committed such an error: Inside of a derived class, I tried to "initialize at declaration" the fields inherited from an abstract base class. The result was that there existed two sets of fields, one is "base" and another is the newly declared ones, and it cost me quite some time to debug.

我认为有一个警告。我曾经犯过这样的错误:在派生类内部,我试图“在声明时初始化”从抽象基类继承的字段。结果是存在两组字段,一组是“base”,另一组是新声明的字段,调试花费了我相当长的时间。

The lesson: to initialize inheritedfields, you'd do it inside of the constructor.

教训:要初始化继承的字段,您需要在构造函数内部进行。

回答by Miroslav Holec

There are many and various situations.

有许多不同的情况。

I just need an empty list

我只需要一个空列表

The situation is clear. I just need to prepare my list and prevent an exception from being thrown when someone adds an item to the list.

情况很清楚。我只需要准备我的列表并防止在有人向列表中添加项目时抛出异常。

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}

I know the values

我知道价值观

I exactly know what values I want to have by default or I need to use some other logic.

我完全知道默认情况下我想要什么值,或者我需要使用其他一些逻辑。

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}

or

或者

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}

Empty list with possible values

具有可能值的空列表

Sometimes I expect an empty list by default with a possibility of adding values through another constructor.

有时我希望默认情况下有一个空列表,可以通过另一个构造函数添加值。

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}

回答by Raedwald

In Java, an initializer with the declaration means the field is always initialized the same way, regardless of which constructor is used (if you have more than one) or the parameters of your constructors (if they have arguments), although a constructor might subsequently change the value (if it is not final). So using an initializer with a declaration suggests to a reader that the initialized value is the value that the field has in all cases, regardless of which constructor is used and regardless of the parameters passed to any constructor. Therefore use an initializer with the declaration only if, and always if, the value for all constructed objects is the same.

在 Java 中,带有声明的初始化程序意味着该字段总是以相同的方式初始化,无论使用哪个构造函数(如果您有多个)或您的构造函数的参数(如果它们有参数),尽管构造函数可能随后更改值(如果它不是最终的)。因此,使用带有声明的初始化程序向读者表明,初始化值是该字段在所有情况下的值,无论使用哪个构造函数,也无论传递给任何构造函数的参数如何。因此,仅当且始终当所有构造对象的值相同时,才使用带有声明的初始化程序。