Since the base type in your example is empty, I agree that the use of the select-type-construct is not a disadvantage.
But if the base type is non-trivial and good enough, you might choose not to extend it, and therefore the select-type-construct is not needed, e.g.:
module solver_mod
implicit none
private
type, public :: point
real :: x, y
end type
abstract interface
subroutine work(a, b)
import
class(point), intent(in) :: a
real, intent(out) :: b
end subroutine work
end interface
public :: solver
contains
subroutine solver(callback, a, b)
procedure(work) :: callback
class(point), intent(in) :: a
real, intent(out) :: b
call callback(a, b)
end subroutine
end module solver_mod
module problem_mod
use solver_mod
implicit none
private
type, extends(point), public :: point3d
real :: z
end type
public :: stick_to_default
public :: try_3d
contains
subroutine stick_to_default(a, b)
class(point), intent(in) :: a
real, intent(out) :: b
print*,'sticking to a plane'
b = sqrt(a%x ** 2 + a%y ** 2)
end subroutine
subroutine try_3d(a, b)
class(point), intent(in) :: a
real, intent(out) :: b
select type (a)
type is (point3d)
print*,'trying 3d'
b = sqrt(a%x ** 2 + a%y ** 2 + a%z ** 2)
class default
call stick_to_default(a, b)
end select
end subroutine
end module problem_mod
program usepoint
use solver_mod
use problem_mod
implicit none
real :: res
type(point) :: a = point(1., 2.)
type(point3d) :: b = point3d(1., 2., 3.)
call solver(stick_to_default, a, res)
print*, 'default result=',res
call solver(try_3d, b, res)
print*, '3d result=',res
end program usepoint
In the class(*) case, the select-type-construct is always required (unless you completely ignore the argument, that is).