简体   繁体   中英

Initializing 'final' Fields of Varying Types in Subclasses

My problem is almost exactly the same as the problem posted here: Abstract class with final uninitialized field and I like the solution. However, my problem is a bit more complicated in that the abstract class has multiple final fields of varying types. For example, I have four int , two int[] , and two double . What would be the best way to force the subclasses to initialize these variables?

Options I've considered:

  • Convert all fields to Strings and pass with a Map
  • Have a really long superclass constructor
  • Create a helper class that would act as a wrapper and encapsulate all the values, then pass an instance of this class to the base class

The first option is not very elegant, and seems a bit complicated, especially with arrays. The second option is very tedious, and the third option just seems like I'm overdoing it.

Is there a "correct" way of doing this? Or if not, which of the three options posed would be the most elegant?

I would go with the second, "Have a really long superclass constructor." If we follow the approach detailed in the question you referenced , the superclass constructor is protected and not meant to be called by anything external to the class hierarchy or package. My feeling always is, once something is not exposed beyond that boundary - ie, is not part of the "API" as it were - then it doesn't matter what it looks like. Let it have eight different parameters of varying types, or even more. Yes, it's visible from within the package, but it's clear from the original solution that that constructor is not meant to be called by anything other than the subclasses. That's another motivation for non- public visibility.

Of course, your instincts for doing something cleaner are correct when it comes to public stuff. The fact that you asked this question at all shows you have the right instincts.

Here's another alternative, assuming you have control of all the classes involved: abstract out the fields in the superclass and declare them in the subclasses, sort of like this...

abstract class SuperClass {
  abstract int[] getFooArray(); // not public!
  abstract int getBar();
}

and then just define the field in each of those subclasses, overriding the methods to return them.

Yes, it will involve some code duplication, but it could very well be cleaner in the end than an unreadably long constructor, and the code you're duplicating isn't very much -- a field, a one-line method to return that field.

However, my problem is a bit more complicated in that the abstract class has multiple final fields of varying types.

I'm not sure I understand the added complexity in your scenario, but I'm interpreting your issue as : I don't want to have a ton of arguments on my abstract constructor. One possible approach is to have a Builder for the abstract class that is used by the concrete sub classes. The builder is then 'passed up' to the abstract constructor for setting the final fields.

When I have a need for an immutable object (all members are final) that takes many different parameters in the constructor I usually use a Builder pattern.

In this case you can make the builders subclass each other and in this way you will still maintain the ability to extend.

For an example you can see the Guava API for ImmutableCollection Builder . Or if you don't require immutability, here is an example of CacheBuilder also taken from the same library:

Cache<Key, Graph> graphs = CacheBuilder.newBuilder()
   .concurrencyLevel(4)
   .weakKeys()
   .maximumSize(10000)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .build(
       new CacheLoader<Key, Graph>() {
         public Graph load(Key key) throws AnyException {
           return createExpensiveGraph(key);
         }
       });

As you can see the use of a builder replaces the need to pass in 6 parameters in the constructor and makes for more readable/usable code.

If you still don't want to use builders I would go with option (3) as that will prevent some of the hassle of maintaining a very long constructor

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM