Reputation: 1670
In my project I see the interface like this. All the model extend the interface. I am wondering what is the use ?
public interface IModel {
<T> T modelTo(Class<T> clazz);
}
public interface IPerson extends IModel {
public String getFirstName();
public void setFirstName(String firstName);
public String getMiddleName();
public void setMiddleName(String middleName);
}
Then in some places in the code I see like
@Override
public void modelJoin(IModel parent, IModel sample) {
//Some code
IPerson sample= sample.modelTo(IPerson.class);
IPerson person = parent.modelTo(IPerson.class);
//Some code
}
Can you explain me the insight of it ?
Upvotes: 0
Views: 202
Reputation: 17973
It looks like the use of the Adapter pattern. The idea is to create a "view" of a class given another class, or adapt one type of class to act as another.
A simple real world example can be that of electrical sockets. In different countries different types of sockets are used. So you use adapters to plug in your phone into an electrical socket it normally doesn't "recognize".
This can of course be modelled using object oriented programming and the adapter pattern as well. Using your IModel interface but naming it IAdaptable it could be used like this.
public interface IAdaptable {
<T> T adaptAs(Class<T> clazz);
}
public interface IChargeAmerican { void chargePhoneInAmerica(); }
public interface IChargeEurope { void chargePhoneInEurope(); }
public class EuropeanSocket implements IAdaptable, IChargeEurope {
public <T> T adaptAs(Class<T> clazz) {
if (clazz.equals(IChargeAmerican.class)) {
return new EuropeanSocketToAmericanSocketAdapter(this);
}
throw new RuntimeException("unknown");
}
public void chargePhoneInEurope() {
;
}
}
public class AmericanSocket implements IChargeAmerican {
public void chargePhoneInAmerica() {
;
}
}
public class EuropeanSocketToAmericanSocketAdapter implements IChargeAmerican {
private EuropeanSocket socket;
public EuropeanSocketToAmericanSocketAdapter(EuropeanSocket socket) {
this.socket = socket;
}
public void chargePhoneInAmerica() {
socket.chargePhoneInEurope();
}
}
And to use it one would simply adapt the european socket to an american one, sort of like plugging in an adapter in between the two.
public void foo() {
EuropeanSocket europe = new EuropeanSocket();
IChargeAmerican murica = europe.adaptAs(IChargeAmerican.class);
murica.chargePhoneInAmerica();
}
This example shows how the adaptAs method creates a link between the two interfaces IChargeAmerican and IChargeEurope. Even though they don't have anything in common the adapter can act as they do.
Now, the EuropeanSocket implements the IAdaptable interface in order to "convert" itself to another known socket. Usually though the class should not be responsible for this. As the example at wikipedia shows, a factory or provider is better suited for this.
Upvotes: 2
Reputation: 1
I can imagine it may have been made that way to allow type casting by passing a Class object as a parameter of other methods calling the modelTo(Class clazz) method, or in other words : having other methods casting IModel objects to any class without even knowing which class they will cast it into (nothing even prevents from passing a Class instance to this method which isn't even a subtype of IModel...)
It would be interesting to know how this modelTo method is implemented. Is there a single, final implementation in an abstract skeleton class ? How does it respond to errors (like passing null as the clazz parameter, or triggering a ClassCastException) ? In other words : could this be an attempt to encapsulate all class casts into a single method, to replace ClassCastExceptions with a custom Exception or something like that ? (ClassCastException being a RuntimeException, it could have been a way to make sure a checked exception is thrown instead to enforce explicit exception handling everywhere in the code, I've already seen projects using such an approach...)
Upvotes: 0
Reputation: 9964
I think that you ask why the method signature
<T> T modelTo(Class<T> clazz);
is used.
The parameter clazz
is used to have the type information inside the method implemented. You can then access the type information very easy.
You can then create an object and return it from the implemented method that has the given class.
The method signature looks a bit clumsy but is helpful as the generic information is missing after compilation (type erasure) and the parameters give you the possibility to access the type information (and thereforethe expected return type).
Upvotes: 1