I think I need a fresh set of eyes on an OOP thing...
# cfml-general
n
I think I need a fresh set of eyes on an OOP thing. (This is on ACF 2023.0.7 but Lucee 5.4.4 does the same thing.) I have a CFC called BaseBean.cfc, that doesn't do anything except run a default constructor and set some basic meta data (date the record was created, IP address of who created it, etc). And I have ActiveUser.cfc that extends BaseBean, like so: component accessors=true extends=BaseBean { init() validate() } I've also got an interface like this: interface IRepository { public struct function insert( required BaseBean theObject ); } and I have a Service that implements IRepository: component implements=IRepository { public struct function insert( required ActiveUser theObject ) { ... } } ActiveUser extends BaseBean, so basic OOP inheritance rules should dictate that ActiveUser "is a" BaseBean. However when I run the code I get: "Data type mismatch.The insert function does not specify the same data type for the theObject argument. model.services.IRepository ColdFusion interface declared data type as BaseBean, whereas model.services.ActiveUserService ColdFusion component specified it as ActiveUser." This seems incorrect to me. Am I forgetting something in my post-conference sleepiness? Or is this a bug in how ACF is compiling things? Thanks.
r
I don't think this will work in CF. I believe you have to create an interface that ActiveUser implements and then require the interface that ActiveUser implements in the insert function.
Copy code
interface IUser
{
    // some code
}

// ActiveUser
component accessors=true extends=BaseBean implements=IUser
{
	init()
	validate()
}
Copy code
interface IRepository
{
	public struct function insert( required IUser theObject );
}

component implements=IRepository
{
	public struct function insert( required IUser theObject )
	{
		...
	}
}
c
Have you tried adding mappedSuperClass=“true” on your BaseBean?
b
@nolanerck, I think what you are counting on is Liskov substitution, the principle that enables you to do something like this in Java:
Basebean user = new ActiveUser();
But that is not what the underlying problem is in this case. I suspect that the problem here concerns the overriding of the method of an interface. In Java, when a class implements an interface, it is required to override all the methods of the interface exactly as they are declared, including the parameter types. So what I think is that the
Basebean
type is required when
Service.cfc
implements
insert()
. You could modify the service CFC by doing something like:
component implements=IRepository
{
public struct function insert( required Basebean theObject )
{
// Cast the object appropriately
if (isInstanceOf(theObject, "ActiveUser")) {
// Use ActiveUser here
}
}
}