Object Orientation
class(name), and optional finalization. The style stays Fortran: objects are still values that copy, and dynamic dispatch is explicit.
Type-bound procedures
A procedure :: name => implementation line inside the type binds a module procedure to the type. Calls use the component selector: obj%name(args). The bound procedure automatically receives the object as its first argument — declared via class(t), intent(inout) :: self — which is how one body serves every instance:
module bank
implicit none
private
public :: account, credit, balance_of
type :: account
private
real :: balance = 0.0
contains
procedure :: credit => credit_acct
procedure :: report => report_acct
end type account
Inheritance: extends
type, extends(parent) :: child reuses the parent's components and procedures and adds new ones. A child is also a parent: any variable declared class(parent) can hold a child instance, and assignment moves the whole record. Drawing on the derived-types lesson, this is inheritance by value extension rather than by reference to shared objects.
Fig. 1 — abstract parent, concrete children, polymorphic class variable.
module shapes
implicit none
private
public :: shape, circle, rectangle, area_of
type, abstract :: shape
private
character(len=16) :: name = "generic"
contains
procedure(area_iface), deferred :: area ! no body here
end type shape
abstract interface
real function area_iface(self)
import :: shape
class(shape), intent(in) :: self
end function area_iface
end interface
type, extends(shape) :: circle
private
real :: radius = 1.0
contains
procedure :: area => circle_area
end type circle
type, extends(shape) :: rectangle
private
real :: width = 1.0, height = 1.0
contains
procedure :: area => rect_area
end type rectangle
contains
real function circle_area(self)
class(circle), intent(in) :: self
circle_area = 3.14159265358979 * self%radius**2
end function circle_area
real function rect_area(self)
class(rectangle), intent(in) :: self
rect_area = self%width * self%height
end function rect_area
real function area_of(s)
class(shape), intent(in) :: s ! ANY extension fits here
area_of = s%area() ! dispatches to the concrete type
end function area_of
end module shapes
program shape_app
use shapes
implicit none
type(circle) :: c
type(rectangle) :: r
c = circle(radius=2.0) ! constructor with keyword component
r = rectangle(width=3.0, height=4.0)
print '(a,f8.3)', 'circle area: ', area_of(c) ! 12.566
print '(a,f8.3)', 'rectangle area:', area_of(r) ! 12.000
end program shape_app
How dispatch works
An class(shape) argument holds a hidden type descriptor; when area_of calls s%area(), the runtime follows the descriptor to the child's binding. This is dynamic dispatch — the same mechanism as a virtual table in C++. The cost is one indirect jump per polymorphic call; the compiler records the cost, and performance-sensitive inner loops keep concrete type(...) arguments to avoid it.
Polymorphism by select type
When behaviour depends on which concrete type sits inside a class(...) variable, select type opens branches per type — the Fortran form of pattern matching. It pairs with reallocation of a class variable, the standard way to build heterogeneous containers:
subroutine describe(s)
class(shape), intent(in) :: s
select type (s)
type is (circle)
print '(a)', 'it is a circle'
type is (rectangle)
print '(a)', 'it is a rectangle'
class default
print '(a)', 'some other shape'
end select
end subroutine describe
Finalization
A type may declare a final procedure, invoked automatically when an instance goes out of scope or is overwritten — the RAII-ish hook for closing files, deallocating resources, or logging. Like destructors, final procedures must be careful: they run implicitly, and debugging code that depends on exact finalization ordering is a known sport.
type :: file_log
integer :: unit
contains
final :: close_log
end type file_log
subroutine close_log(self)
type(file_log), intent(inout) :: self
close (self%unit)
end subroutine close_log
OOP style guide: keep inner loops on type(...); use class(...) only at interface boundaries; make components private behind a module; prefer generic interfaces (modules lesson) over inheritance when the family is small. The study-projects page builds a full object-oriented application to cement these patterns.
contains
subroutine credit_acct(self, amount)
class(account), intent(inout) :: self
real, intent(in) :: amount
self%balance = self%balance + amount
end subroutine credit_acct
real function balance_of(a)
class(account), intent(in) :: a
balance_of = a%balance
end function balance_of
subroutine report_acct(self)
class(account), intent(in) :: self
print '(a,f10.2)', 'balance: ', self%balance
end subroutine report_acct
end module bank
program banking
use bank
implicit none
type(account) :: a
call a%credit(100.0) ! method call bound to the object
call a%credit(50.0)
call a%report() ! "balance: 150.00"
print '(a,f10.2)', 'read via accessor: ', balance_of(a)
end program banking
Note the encapsulation: private components are invisible outside the module, so the balance can only change through credit. The public API — types and procedures — is exactly what the use bank line in a consumer sees. This is textbook data hiding with zero framework.