前回: https://qiita.com/Bluepost59/items/ca560c49a8c19484db9d
果てしなく間が空いてしまったが、今回は一歩進んで継承と抽象クラスについてまとめたいと思う。
※「そもそも抽象クラスって何」って人はこれを見て下さい
https://qiita.com/Bluepost59/items/eef6f48fdd322b0b9791
#継承
継承は簡単で、typeの後にextendsキーワードをつけるだけでよい。
type,extends(parent_class) :: child_class
integer param1
!...
contains
procedure :: method1 => child_method1
!...
end type child_class
#抽象クラス
抽象クラス自体はtypeにabstract属性を付けるだけでよい。
関数やサブルーチンをつけるところがかなりややこしい。procedure文だけでは不十分で、interface文で引数や戻り値を設定する必要がある。
type,abstract :: absclass
integer param2
!...
contains
procedure(absmethod1),deferred :: method1
procedure(absmethod2),deferred :: method2
!...
end type absclass
!引用仕様(戻り値、引数)の定義
interface
subroutine absmethod1(self,subparam)
import absclass
class(absclass) self
integer subparam
end subroutine absmethod1
double precision function absmethod2(self,subparam)
import absclass
!...
end function absmethod2
end interface
ポイントはクラス本体の宣言と引用仕様の宣言が相互参照のようになっているところだ。はっきりいって謎な仕様ではある。
何はともあれ、これで抽象クラスが定義された。この子クラスではmethodをオーバーライドしなければコンパイルエラーとなる。
#具体例
抽象クラスgirlと、それを継承したクラスidolを実装した。girlはnameおよびそのセッタとゲッタ、idolはそれに加えてgroupとセッタ、ゲッタを実装してある。
module mod_idol
implicit none
!---------------------------------------------------
! 抽象クラス girl
type,abstract :: girl
private
character(len=60) :: name = "noname"
contains
procedure(girl_set_name),deferred :: set_name
procedure(girl_get_name),deferred :: get_name
end type girl
! 引用仕様の宣言
interface
subroutine girl_set_name(self,str)
import girl
class(girl) self
character(len=60) str
end subroutine girl_set_name
character(len=60) function girl_get_name(self)
import girl
class(girl) self
end function girl_get_name
end interface
!---------------------------------------------------
! クラス idolの宣言部
type,extends(girl) :: idol
private
character(len=60) group
contains
procedure :: set_name => idol_set_name
procedure :: get_name => idol_get_name
procedure :: join => idol_join
procedure :: get_group => idol_get_group
end type idol
contains
!---------------------------------------------------
! クラス idolの実装部
subroutine idol_set_name(self,str)
class(idol) self
character(len=60) str
self%name = str
end subroutine idol_set_name
character(len=60) function idol_get_name(self)
class(idol) self
idol_get_name = trim(self%name)
end function idol_get_name
subroutine idol_join(self,group)
class(idol) self
character(len=60) group
self%group = group
end subroutine idol_join
character(len=60) function idol_get_group(self)
class(idol) self
idol_get_group = trim(self%group)
end function idol_get_group
end module mod_idol
program main
use mod_idol
implicit none
type(idol) :: myidol
character(len=60) :: myname = "Momoka"
character(len=60) :: myunit = "momo_pear_berry"
call myidol%set_name(myname)
call myidol%join(myunit)
write(6,*) "name:", myidol%get_name()
write(6,*) "unit:", myidol%get_group()
end program main
gist: https://gist.github.com/Bluepost59/be1975d070e3451c0aa51ab4d6d160f5
#感想
- コードがえげつなく長くなってしまった。もう少し短く書けないものか。
- とはいえ古文と揶揄されるfortranでもOOPが一通り実装できたので満足。
正直C++の方が楽
#参考サイト
NAG
http://www.nag-j.co.jp/nagfor/np52_manual/np52_manual_10_2.html