Java Theory and Practice: is that your final answer?

Source: Internet
Author: User
Tags array object constructor final int size string version variable
The final keyword is often misused-it is overused when declaring classes and methods, but is not used when declaring instance fields. This month, Java practitioner Brian Goetz explored some guidelines for the effective use of final.
Like the const keyword in its "cousin"-C, final means something different according to the context. The final keyword can be applied to a class, method, or field. When applied to a class, it means that the class cannot be regenerated to subclasses. When applied to a method, it means that the method cannot be overridden by a quilt class. When applied to a field, it means that the value of the field must only be assigned once in each constructor and that the value will never change thereafter.

Most Java text appropriately describes the usage and consequences of using the final keyword, but rarely provides guidelines on when to use final and frequency content. In my experience, final was very overused for classes and methods (often because developers mistakenly believe that this would improve performance), and where they came in-declaring class instance variables-was not used.

Why is this class final?
It is common for developers to declare a class final without giving a description of why the decision was made, especially in open source projects. After a while, especially if the original developer no longer participates in the maintenance of the code, other developers will always ask "Why is class X declared final?" ”。 Usually no one knows that when someone does know or likes to guess, the answer is almost always "because it makes it run faster". The general understanding is that declaring a class or method final would make it easier for the compiler to inline method calls, but that understanding is incorrect (or at least vastly exaggerated).

Final classes and methods can be very cumbersome to program-they limit the ability to choose to reuse existing code and extend existing classes. Sometimes there are good reasons to declare a class as final (such as coercion invariance), and the benefits of using final will be greater than the inconvenience. Performance improvement is almost always a bad reason to spoil good object-oriented design principles, and it's really a bad trade-off when performance is small or not improved at all.

Premature optimization
For performance reasons, it is a bad idea to declare a method or class final in the early stages of a project, for several reason. First, early stage design is not a time to consider cyclic computing performance optimizations, especially when such decisions may constrain your use of final design. Second, the performance advantage obtained by declaring a method or class final is usually zero. Moreover, declaring complex, stateful classes as final is detrimental to object-oriented design and leads to large and all-encompassing classes, since they cannot easily be made into smaller, more compact classes.

As with many myths about Java performance, declaring a class or method as final leads to better performance, a misconception that is widely accepted but rarely tested. The argument is that declaring a method or class final means that the compiler can more actively inline the method invocation because it knows at runtime that this is the version of the method to invoke. But this is clearly not true. Just because class X is compiled into final class Y does not mean that the same version of Class Y will be loaded at runtime. Therefore, the compiler cannot securely inline such a cross class method call, whether final or not. The final keyword is redundant only if the method is private, and the compiler is free to inline it.

On the other hand, the run-time environment and the JIT compiler have more information about what class is actually loaded than the compiler can make much better tuning decisions. If the runtime environment knows that a class that inherits Y is not loaded, it can safely inline the call to the Y method, regardless of whether Y is final (as long as it can invalidate the JIT-compiled code when it is subsequently loaded into the Y subclass). The fact is that, although final is useful for a "dumb" run-time optimizer that does not perform any global correlation analysis, its use does not actually support too many compile-time optimizations, and the intelligent JIT performs run-time optimizations without it.

Déjà vu-Re-recall the Register keyword
Final is very similar to the Register keyword that is not in use in C when it is used for optimization decisions. Allowing programmers to help optimizer this desire contributed to the Register keyword, but in fact it was found to be not very useful. As we are willing to believe in other areas, compilers generally do better than people in making code tuning decisions, especially on current RISC processors. In fact, most C compilers completely ignore the Register keyword. The previous C compiler ignored it because the compilers were not optimized at all; today's compiler ignores it because the compiler does not use it to make better decisions about optimizations. In either case, the Register keyword does not add any performance benefits, and is similar to the final keyword applied to Java classes or methods. If you want to optimize your code, stick with optimizations that can dramatically improve performance, such as using efficient algorithms and not performing redundant computations-leaving the loop calculation optimizations to the compiler and JVM.

Use Final to maintain invariance
While performance is not a good reason to declare a class or method final, there are sometimes plenty of reasons to write the final class. Most commonly, final guarantees that the classes that are designed to remain unchanged are not changed. Invariant classes are useful for simplifying the design of object-oriented programs-unchanging objects require less defensive coding and do not require strict synchronization. You will not build the idea in your code that the class is immutable and then let someone inherit it in a way that makes it mutable. Declaring immutable classes as final guarantees that such errors do not sneak into your program.

Another reason that final is used for classes or methods is to prevent links between methods from being corrupted. For example, suppose that the implementation of a method of class X assumes that method M will work in some way. Declaring X or M final will prevent the derived class from redefining M in this way, causing X to work abnormally. Although it may be better to implement X without these internal dependencies, this is not always feasible, and using final can prevent such incompatible changes in the future.

If you must use the final class or method, note why you are doing so
In any case, when you do choose to declare the method or class final, note why you did so. Otherwise, future maintainers may wonder whether there are good reasons for doing so (because they are often not) and are bound by your decision, but at the same time do not know the motivation for doing so is to gain any benefits. In many cases, the decision to declare a class or method as final has been deferred until later in the development process, when you already have better information about how classes interact and how they might be inherited. You may find that you do not need to declare the class final at all, or you can refactor the class to apply final to a smaller, simpler class.

Final field
The final field and final class or method are so different that I think it's unfair to let them share the same keywords. The final field is a read-only field to ensure that its value is set only once at build time (or, for the static final field, when the class is initialized). As discussed earlier, for final classes and methods, you will always ask yourself if you really need to use final. For the final field, you will ask yourself the opposite question-does this field really need to be variable? You may be surprised at how often the answer is "no need".

Document Description Value
The final field has several benefits. For developers who want to use or inherit your class, declaring a field as final has important documentation benefits-not only to help explain how the class works, but also to get the compiler's help in enforcing your design decisions. Unlike the final method, declaring the final field helps the optimizer make better tuning decisions, because if the compiler knows that the value of the field does not change, it can safely cache the value in the register. The final field also provides an additional level of security by having the compiler force the field to read-only.

In extreme cases, a class whose fields are final primitives or final references to immutable objects, the class itself becomes immutable-in fact it is a very convenient situation. Even if the class is not completely unchanged, making certain parts of the state unchanged can greatly simplify development-you do not have to keep the current value of the final field in view or make sure that no one else is synchronizing this part of the object's state.

So why is the final field so inadequate to use? One reason is that they are a bit of a hassle to use correctly, especially for object references whose constructors can throw exceptions. Because the final field must be initialized only once in each constructor, if the final object references a constructor that might throw an exception, the compiler may complain that the field is not initialized. Compilers are generally more intelligent enough to discover that initialization occurs only once in each branch of two mutually exclusive branches of code (for example, if...else blocks), but it is generally not so "tolerant" to try...catch blocks. For example, most Java compilers do not accept the code in Listing 1:

Listing 1. Invalid initialization of final reference field
public class Foo {
Private final thingie thingie;

Public Foo () {
try {
Thingie = new Thingie ();
}
catch (Thingieconstructionexception e) {
Thingie = Thingie.getdefaultthingie ();
}
}
}




But they will accept the code in Listing 2, which is equivalent to:

Listing 2. Valid initialization of final reference field
public class Foo {
Private final thingie thingie;

Public Foo () {
Thingie Tempthingie;
try {
Tempthingie = new Thingie ();
}
catch (Thingieconstructionexception e) {
Tempthingie = Thingie.getdefaultthingie ();
}
Thingie = Tempthingie;
}
}




Limitations of final field
The final field still has some serious limitations. Although an array reference can be declared final, the elements of that array are not. This means that the class that exposes public final array fields or their methods of returning references to those fields (for example, the Dangerousstates class shown in Listing 3) is not immutable. Similarly, although an object reference can be declared as a final field, the object it references may still be mutable. If you want to use the final field to create an immutable object, you must prevent the reference of an array or mutable object from "escaping" your class. A simple way to do this without having to clone the array repeatedly is to convert the array to a list, such as the Safestates class shown in Listing 3:

Listing 3. Exposing an array reference makes a class variable
Not immutable--the states array could is modified by a malicious
Caller
public class Dangerousstates {
Private final string[] states = new string[] {"Alabama", "Alaska", ...};

Public string[] GetStates () {
return states;
}
}


Immutable--Returns an unmodifiable List instead
public class Safestates {
Private final string[] states = new string[] {"Alabama", "Alaska", ...};
Private Final List Statesaslist
= new Abstractlist () {
Public Object get (int n) {
return states[n];
}

public int size () {
return states.length;
}
};

Public List getstates () {
return statesaslist;
}
}




Why not inherit final to apply to arrays and referenced objects, similar to the use of Const in C and C + +? The semantics and use of const in C + + are rather confusing, depending on where it appears in an expression. Java Architects managed to "rescue" us from this confusion, but unfortunately they created some new confusion in the process.

Conclusion
There are some basic guidelines you can follow to effectively use the final for classes, methods, and fields. Notably, don't try to use final as a performance management tool; To improve the performance of your programs, there are better and less restrictive approaches. Use final in the basic semantics that reflect your program: used to indicate that these classes will be immutable or that those fields will be read-only. If you choose to create a final class or method, make sure you have a clear record of why you are doing this-your colleagues will appreciate it.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.