Classes

A class is a data structure that encapsulates both data (variables) and behavior (procedures). The only difference between a derived type and a class is that a class may contain procedures as well as variables.

The major difference between a class and a module is that modules cannot be instantiated; that is, variables can be declared of any type it may encompass, but not of the module itself.

OOP Terminology

An instance of a type or class is a variable of that type/class.

type(mytype)   :: A, B

A and B are instances of mytype. The variables and procedures that belong to the class are often called members of the class.

Types with Procedures

Types containing type-bound procedures were introduced in Fortran 2003. They are nearly synonymous with methods in other languages. If a type-bound procedure is public it can be called in the conventional way from a unit that creates a variable of the type. If the procedure is private then it can be accessed only via an instance of the type. As for modules, the default is public.

Type-bound procedures are declared using CONTAINS and the PROCEDURE keyword.

TYPE mytype
   REAL      ::x,y
   CONTAINS
      PROCEDURE ::init=>init_class
      PROCEDURE :: write=>write_class
      PROCEDURE :: reset
END TYPE

The => operator is optional. It means that the procedure on the left-hand side will be used through an instance to invoke the actual procedure on the right-hand side.

TYPE(mytype) :: v
   v%init(arg1,arg2)
   v%write()
   v%reset(arg)

Instance Parameters

In Fortran the instance variable must be passed explicitly as the first parameter to the procedure, unless the PASS keyword is utilized. The instance variable must be declared as CLASS rather than TYPE. When we invoke the procedure we do not explicitly pass the instance argument. If it does not need to be passed at all (for the equivalent of a class method in other languages), the NOPASS attribute can be added, as discussed below.

Filling out the procedures gives us

MODULE mytype_class
IMPLICIT NONE
   TYPE MyType
      INTEGER   ::i,j
      REAL      ::x,y
      CONTAINS
         PROCEDURE :: init=>init_class
         PROCEDURE :: write=>write_class
         PROCEDURE :: reset
   END TYPE

   PRIVATE init_class

   CONTAINS

   SUBROUTINE init_class(self,stuff1,stuff2)
      CLASS(MyType), INTENT(INOUT) :: self
      REAL,          INTENT(IN)    :: stuff1, stuff2
      self%i=0; self%j=0
      self%x=stuff1; self%y=stuff2
   END SUBROUTINE

   SUBROUTINE WRITE_CLASS(self,iunit)
      CLASS(MyType), INTENT(IN) :: self
      INTEGER,       INTENT(IN) ::iunit

      WRITE(*,*) "Integers ", self%i, self%j
      WRITE(*,*) "Reals", self%x, self%y
   END SUBROUTINE

   SUBROUTINE reset(self,i1,i2,stuff1,stuff2)
      CLASS(MyType), INTENT(INOUT) :: self
      INTEGER,       INTENT(IN)    :: i1, i2
      REAL,          INTENT(IN)    :: stuff1, stuff2
      self%i=i1; self%j=i2
      self%x=stuff1; self%y=stuff2
   END SUBROUTINE
END MODULE


There is no reserved word in Fortran for the class instance variable. Some authors use “self” as is conventional in Python. Others use “this” to imitate C++ and Java. Others prefer their own conventions.

Invoking Class Methods

The write_class procedure is not private so the second two calls are equivalent.

call myvar%init(x,y)
call write_class(myvar,11)
callmyvar%write(12)

Even in languages such as C++, where the “real” form write_class(this,param) is not accessible, the compiler still constructs a “real” function with a unique name for the method and invokes it in the usual way. The class name is typically attached as well as the instance variable appearing explicitly. The “true” name of the method is said to be “mangled.” Name mangling is also used to distinguish overloaded functions which can seemingly take different types for the same calling sequence. Various language rules, such as not passing the instance variable, are often called “syntactic sugar” by computer scientists, since their aim is to simplify the human-written code.

PASS and NOPASS

If it is desirable to pass the instance variable at some position other than the first, the PASS attribute may be used.

module mytype_mod
implicit none
TYPE mytype
   contains
      procedure, pass(z) :: mysub
end TYPE

   contains

      subroutine mysub(y,z)
      class(mytype) :: z
      real          :: y
         print *, y,z
      end subroutine
end module

In this case z will represent the instance variable, so the procedure will be invoked like

call z%mysub(y)

For cases where no instance variable is required at all, the NOPASS attribute may be used. This creates something analogous to a type of method that is sometimes called a class method in other languages.

module mytype_mod
implicit none
TYPE mytype
   contains
      procedure, nopass :: mysub
end TYPE

   contains

      subroutine mysub(y,z)
      real          :: y,z
         print *, y,z
      end subroutine
end module

Invoke this with

call t%mysub(w,x)

If mysub is public it can even be called with

call mysub(w,x)

in which case it is effectively equivalent to a procedure in a module

Previous
Next