Reputation: 105
I am struggling a bit with class casting. Let me set the scene. I have java server code that uses a service and orchestrator layer. A request comes into the service layer in a bean format (java class aligned with the front end view), and then i have a bunch of domainBeanMapper classes which take a bean format object and translate it to a domain format object. Eg UserBean has a dateOfBirth represented by a string, while User has the dateOfBirth represented by a Date so the UserMapper.java will make the date string into a date object. So for each object in the system i have a *.java, *Bean.java and *Mapper.java (User.java, UserBean.java, userMapper.java).
in applicationContext i hold the relationship from each object to their mapper, like this:
<util:map id="domainBeanMappers">
<entry key="UserBean" value-ref="userMapper" />
<entry key="User" value-ref="userMapper" />
.....
and then i define the mapper:
<bean id="userMapper" class="com.me.mapping.UserMapper" parent="baseDomainBeanMapper"/>
I call the domain bean mappers like this from my service layer:
UserBean userBean = (UserBean) getDomainBeanMapper().mapDomainToBean(user);
In the run of this code I find the mapper object that I want like this:
DomainBeanMapper mapper = findApplicableMapper(myObject.getClass().getName());
if (mapper == null) {
mapper = findApplicableMapper(myObject.getClass().getSimpleName());
}
where findApplicableMapper works like this:
private DomainBeanMapper findApplicableMapper(String string) {
return domainBeanMappers.get(string);
}
For the last few years, this has worked like a charm. For whatever object I want in the system i can easily pick out the relevant mapper and then translate from my bean format to domain format and vice-versa, based on the .getClass() call of any instance.
Now, I have a new requirement that is giving me trouble. I want to be able to translate from my User object to several sub-objects based on a parameter. So for some calls, i want just the id, firstName and lastName back. For other calls I want more fields, but still not the whole object, and then for some calls I want the whole object back like before. I don't want to go down the sub-objects path and end up with UserLight, UserLightWithName, UserLightWithNameButNoAddress, ... argh nightmare.
So instead I was hoping to create a bunch of interfaces representing "views". So the request comes in with the ViewType of Basic, and that means I want the user's personal details and address. So I wrote an interface called UserBasic, got User to implement it, and added a mapping from UserBasic to UserBasicMapper, and from UserBasicBean to UserBasicMapper, in the hope that I could make the translate call like this:
UserBasicBean restrictedUserReturn = (UserBasicBean) getDomainBeanMapper().mapDomainToBean((UserBasic)user);
but this doesn't work because getClass() always returns the instances class, not the interface that it has been cast to. I guess I can always add another paramater to the mapDomainToBean call which is the class that I want used, but the codebase is quite massive and I will need to touch on every call if i make that change.
So basically I'm looking for a way to cast an instance to an interface type and then find that interface type? Is this possible??
crosses fingers...
Upvotes: 5
Views: 306
Reputation: 13930
Instead of making interfaces that the user implements, why not make proxy classes that represent the limited versions? For example,
class UserBasic {
private User user;
public UserBasic(User user) {
this.user = user;
}
public String getFirstName() {
return user.getFirstName();
}
...
}
Then you can add a getUser() { return user; }
to get the full instance back.
This way you should be able to keep everything else the same and make a trade-off for the class over interface.
Upvotes: 3
Reputation: 5354
Interface does not represent view - it defines a behaviour. I guess you need some kind of factory (ckech these links for inspiration https://en.wikipedia.org/wiki/Factory_method_pattern , https://sourcemaking.com/design_patterns/abstract_factory) here (or builder) If you say something about 'sub-objects' - try to extend UserBean class
class BaseUserBean extends UserBean {}
class AnotherUserBean extends UserBean {}
put the common stuff for all users into base class and something really specific to each of your sub-users let's say into children classes and then
UserBean u = getDomainBeanMapper().mapDomainToBean(user);
...
//method mapToDomain should check your param and return the type you need
//ViewType can be a part of user or a separate param
UserBean mapDomainToBean(User user, ViewType vt) {
if (vt == ViewType.BASE) {
BaseUserBean obj = BaseUserBean ();
// mapping here (set fields you need)
return obj;
}
if (vt == ViewType.ANOTHER) {
AnotherUserBean obj = AnotherUserBean ();
// mapping here (set fields you need)
return obj;
}
...
}
Upvotes: 0
Reputation: 6414
You can make something like writing few methods that differ with the paramenter type, example:
public class Test
{
public static void n(Number n) {
System.out.println("Number");
}
public static void n(Integer n) {
System.out.println("Integer");
}
public static void main(String[] args)
{
n(3); //will call the n(Integer n);
n((Number)3); //will call the n(Number n);
}
}
So yes, you can find out which type you have casted to by making multiple methods each taking proper parameter type and then processing it accordingly to type.
Upvotes: 0
Reputation: 571
I am not sure but you can use instanceof keyword to check which type of object it is.
Upvotes: 0