So far, we’ve been able to access any type components or methods from the main program without issues. This is also the default behavior: all type components and methods are visible (public), unless otherwise specified. Recall from section 4.2.4 that this is the same behavior as with module variables and procedures, where we used public and private attributes to explicitly specify which entities can be accessed from outside of the module, and which can’t.
- If no
privateorpublicattribute is specified in the declaration, all components and methods are public by default. - A single
privatestatement inside the derived type definition means that all following components will be declared asprivateby default. The same is true for a singlepublicstatement.
An interesting caveat to private type components is that they make it impossible to use a default type constructor. Take the type in the next listing, for example.
Listing 8.8 A derived type with a private component
type :: Person
character(len=20) :: name
integer, private :: age
end type Person
If you try to initialize it as
type(Person) :: some_person
some_person = Person('Jill', 32)
the compiler will yell at you:
derived_type_private_error.f90:12:16:
some_person = Person('Jill', 32)
1
Error: Component ‘age’ at (1) is a PRIVATE component of ‘person’
If, on the other hand, you try some_person = Person('Jill'), this happens:
derived_type_private_error.f90:12:16:
some_person = Person('Jill')
1
Error: No initializer for component ‘age’ given in the structure constructor at (1)
The compiler won’t budge on this. We can’t pass the age parameter because the component is declared as private, and we can’t not pass it because the default constructor needs it!
There are two ways to work around this:
- Set a default value for the private component inside the type definition, like we did in section 8.2.5.
- Override the default type constructor with a custom function, like we did in section 8.2.6.
While the first approach is easier to code, it may not be suitable for derived types with many components, and for those components that don’t have a meaningful default value. The second approach involves more work but is more generally applicable for anything other than the simplest toy apps. We’ll revisit private and public attributes again in chapter 10 when we explore defining built-in operators (such as +, -, etc.) for derived types.
Exercise 1: Working with private components
In some applications, it may be useful to protect certain type components from being directly accessed or modified from the client code; for example, by the user of your software library. Sometimes, you’ll want to add some additional instructions or data processing when setting the value of a component. Other times, you may want to validate the value of the component on access.
In this exercise, take the derived type Person with the private age component from listing 8.8, and define methods to get (read) and set (modify) the value of age. Furthermore, raise an error if the input argument to the set method is invalid; for instance, if input age is a negative number.
You can find the solution in the “Answer key” section near the end of this chapter.
Using the so-called getter and setter methods to read to and write from, respectively, a type component is one of the pillars of object-oriented programming: encapsulation. This approach allows you to hide the internal details of the component while allowing access via well-defined methods. It’s especially advantageous when writing more complex and robust applications. Here are some cases:
- When the internal implementation of a component changes, your getters and setters will still work as expected without any modifications.
- If you need to check whether a type component is allocated in memory or initialized, you can do so inside the
getmethod. - If assigning a value to a type component requires any additional calculation or housekeeping, such as counting the number of elements in an array, you could include it in the setter method.
However, whether you’ll use encapsulation or not is totally up to you and the app that you build.