Good or bad practice? Initializing objects in gett

2019-01-20 21:20发布

I have a strange habit it seems... according to my co-worker at least. We've been working on a small project together. The way I wrote the classes is (simplified example):

[Serializable()]
public class Foo
{
    public Foo()
    { }

    private Bar _bar;

    public Bar Bar
    {
        get
        {
            if (_bar == null)
                _bar = new Bar();

            return _bar;
        }
        set { _bar = value; }
    }
}

So, basically, I only initialize any field when a getter is called and the field is still null. I figured this would reduce overload by not initializing any properties that aren't used anywhere.

ETA: The reason I did this is that my class has several properties that return an instance of another class, which in turn also have properties with yet more classes, and so on. Calling the constructor for the top class would subsequently call all constructors for all these classes, when they are not always all needed.

Are there any objections against this practice, other than personal preference?

UPDATE: I have considered the many differing opinions in regards to this question and I will stand by my accepted answer. However, I have now come to a much better understanding of the concept and I'm able to decide when to use it and when not.

Cons:

  • Thread safety issues
  • Not obeying a "setter" request when the value passed is null
  • Micro-optimizations
  • Exception handling should take place in a constructor
  • Need to check for null in class' code

Pros:

  • Micro-optimizations
  • Properties never return null
  • Delay or avoid loading "heavy" objects

Most of the cons are not applicable to my current library, however I would have to test to see if the "micro-optimizations" are actually optimizing anything at all.

LAST UPDATE:

Okay, I changed my answer. My original question was whether or not this is a good habit. And I'm now convinced that it's not. Maybe I will still use it in some parts of my current code, but not unconditionally and definitely not all the time. So I'm going to lose my habit and think about it before using it. Thanks everyone!

9条回答
We Are One
2楼-- · 2019-01-20 22:15

Let me just add one more point to many good points made by others...

The debugger will (by default) evaluate the properties when stepping through the code, which could potentially instantiate the Bar sooner than would normally happen by just executing the code. In other words, the mere act of debugging is changing the execution of the program.

This may or may not be a problem (depending on side-effects), but is something to be aware of.

查看更多
仙女界的扛把子
3楼-- · 2019-01-20 22:18

I was just going to put a comment on Daniel's answer but I honestly don't think it goes far enough.

Although this is a very good pattern to use in certain situations (for instance, when the object is initialized from the database), it's a HORRIBLE habit to get into.

One of the best things about an object is that it offeres a secure, trusted environment. The very best case is if you make as many fields as possible "Final", filling them all in with the constructor. This makes your class quite bulletproof. Allowing fields to be changed through setters is a little less so, but not terrible. For instance:

class SafeClass
{
    String name="";
    Integer age=0;

    public void setName(String newName)
    {
        assert(newName != null)
        name=newName;
    }// follow this pattern for age
    ...
    public String toString() {
        String s="Safe Class has name:"+name+" and age:"+age
    }
}

With your pattern, the toString method would look like this:

    if(name == null)
        throw new IllegalStateException("SafeClass got into an illegal state! name is null")
    if(age == null)
        throw new IllegalStateException("SafeClass got into an illegal state! age is null")

    public String toString() {
        String s="Safe Class has name:"+name+" and age:"+age
    }

Not only this, but you need null checks everywhere you might possibly use that object in your class (Outside your class is safe because of the null check in the getter, but you should be mostly using your classes members inside the class)

Also your class is perpetually in an uncertain state--for instance if you decided to make that class a hibernate class by adding a few annotations, how would you do it?

If you make any decision based on some micro-optomization without requirements and testing, it's almost certainly the wrong decision. In fact, there is a really really good chance that your pattern is actually slowing down the system even under the most ideal of circumstances because the if statement can cause a branch prediction failure on the CPU which will slow things down many many many more times than just assigning a value in the constructor unless the object you are creating is fairly complex or coming from a remote data source.

For an example of the brance prediction problem (which you are incurring repeatedly, nost just once), see the first answer to this awesome question: Why is it faster to process a sorted array than an unsorted array?

查看更多
劫难
4楼-- · 2019-01-20 22:20

The downside that I can see is that if you want to ask if Bars is null, it would never be, and you would be creating the list there.

查看更多
登录 后发表回答