I'm trying to create an interface for a set of classes,
but I am failing in finding a solution, which
prevents copying almost similar code for each new method in the
interface.
The interface
public interface P6BO<T extends BusinessObject> {
String[] getFields();
void create(T businessObject) throws P6BOException;
void delete(T businessObject) throws P6BOException;
}
30+ implementations of the interface.
public class ActivityBO implements P6BO<Activity> {
...
#Override
public void create(Activity activity) throws P6BOException {
...
}
#Override
public void delete(Activity activity) throws P6BOException {
...
}
}
Container class to initialize all the implementations and
provide a single point of access to each of them.
public class P6Bom {
public final ActivityBO activity = new ActivityBO();
public final EpsBO eps = new EpsBO();
public final ResourceBO resource = new ResourceBO();
public P6Bom(P6Info p6Info) throws P6BOException {
activity.activate(p6Info, p6Cache, p6Buffer);
eps.activate(p6Info, p6Cache, p6Buffer);
resource.activate(p6Info, p6Cache, p6Buffer);
}
...
public void create(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
activity.create((Activity) businessObject);
} else if (businessObject instanceof EPS) {
eps.create((EPS) businessObject);
} else if (businessObject instanceof Resource) {
resource.create((Resource) businessObject);
}
}
public void delete(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
activity.delete((Activity) businessObject);
} else if (businessObject instanceof EPS) {
eps.delete((EPS) businessObject);
} else if (businessObject instanceof Resource) {
resource.delete((Resource) businessObject);
}
}
public P6BO<? extends BusinessObject> getBO(BusinessObject businessObject) throws P6BOException {
if (businessObject instanceof Activity) {
return activity;
} else if (businessObject instanceof EPS) {
return eps;
} else if (businessObject instanceof Resource) {
return resource;
} else {
throw new P6BOException("not implemented.");
}
}
}
The test class
public class Test() {
/* Works: but is not generic (I cannot call the delete method for any BusinessObject). */
Activity activity = new Activity("MyNewActivity");
P6Bom.activity.create(activity);
P6Bom.activity.delete(activity);
/* Works: but results in a double administration in the P6Bom */
Activity activity = new Activity("MyNewActivity");
P6Bom.create(activity);
P6Bom.delete(activity);
/* Compiler error
The method delete(capture#1-of ? extends BusinessObject)
in the type P6BO<capture#1-of ? extends BusinessObject>
is not applicable for the arguments (Activity)
*/
p6Bom.getBO(activity).delete(activity);
}
What would be an elegant solution to prevent the repetitive administration of each method in the P6Bom interface?
You can use a map:
public class P6Bom {
public final Map<Class<? extends P6BO<? extends BusinessObject>>, P6BO<? extends BusinessObject>>> classMap;
public P6Bom(P6Info p6Info) throws P6BOException {
classMap = new HashMap<>();
classMap.put(ActivityBo.class, new ActivityBO());
...
for(var bo: classMap.values()) {
bo.activate(p6Info, p6Cache, p6Buffer);
}
}
...
public void create(BusinessObject businessObject) throws P6BOException {
getBO(businessObject).create(businessObject);
}
public P6BO<? extends BusinessObject> getBO(BusinessObject businessObject) throws P6BOException {
if (classMap.containsKey(businessObject.getClass())) {
return classMap.get(businessObject.getClass());
} else {
throw new P6BOException("not implemented.");
}
}
}
I forget if the <? extends BusinessObject> allows you to do that. If the compiler complains, omit that part in the classMap declaration and the getBO signature and ignore the warnings ;-)
The thing to remember here is that in the bytecode, the generics are removed, so the actual method signature allows you always to pass in BusinessObjects. Once inside the method, it will be cast and throw a RuntimeException if the class doesn't match, but from the compiler/interface side, it is perfectly possible to write code like that.
This seems like a classical factory and strategy pattern issue, where the Open/Closed principle is violated. You want to extend your application, but without modifying it.
#kutschkem's solution is so far the most elegant way. In addition I would suggest to instantiate the strategy/business object manager when the first business object is created.
This will free your hands from enumerating the BOM's, because the suggested way, the Open/Closed principle is still violated. Once a new business object is introduced, its respective manager needs to be added in the class.
The solution could be the following:
public class P6Bom {
private final Map<String, P6BO<? extends BusinessObject>> classMap;
private final P6Info info;
private final P6Cache cache;
private final P6Buffer buffer;
public P6Bom(P6Info info, P6Cache cache, P6Buffer buffer) {
this.classMap = new HashMap<>();
this.info = info;
this.cache = cache;
this.buffer = buffer;
}
public <T extends BusinessObject> void create(T businessObject) throws P6BOException {
this.getBO(businessObject).create(businessObject);
}
public <T extends BusinessObject> P6BO<T> getBO(T businessObject) throws P6BOException {
if (!this.classMap.containsKey(businessObject.getClass().getName())) {
try {
var bo = (P6BO<T>)Class.forName(businessObject.getClass().getPackageName() + ".bo." + businessObject.getClass().getSimpleName() + "BO").getConstructors()[0].newInstance();
bo.activate(this.info, this.cache, this.buffer)
this.classMap.put(
businessObject.getClass().getName(),
bo
);
} catch (Exception e) {
throw new P6BOException("not implemented.");
}
}
return (P6BO<T>)this.classMap.get(businessObject.getClass().getName());
}
}
Of course, for that solution there need to be a convention (or configuration) how strategies (e.g. ActivityBO) could be found for BusinessObjects. In the example, they live in a subpackage called bo.
Related
I am trying to achieve Singleton Instance with Abstract class in Java. I have read like implementing it as a LazyLoad is the best practice. I could not make it as I am not much used to this pattern and even Java.
getInstance() is always null, I am not sure why?
I don't think this follows lazy loading as the instance is constructed.
Any additional comments are welcome apart from this as I am completely new to Java and worked on c# before.
Here is what I have,
Interface:
public interface IConditionAppender{
public String Append();
}
Abstract
public abstract AppenderBase {
private static IConditionAppender instance;
protected AppenderBase(IConditionAppender instance)
{
this.instance = instance;
}
public static IConditionAppender getInstance(){ return instance; }
}
Implementation
public final class AndAppender extends AppenderBase implements IConditionAppender {
private AndAppender()
{
super(new AndAppender())
}
#Override
public String Append()
{
return " AND ";
}
}
Test
System.out.println(AndAppender.getInstance().Append());
Here is how AndAppender can be implemented
public final class AndAppender implements ConditionAppender {
private static final AndAppender instance;
public static AndAppender getInstance() {
if (instance == null)
instance = new AndAppender();
return instance;
}
private AndAppender() { }
#Override
public String append() {
return " AND ";
}
}
The same approach for OrAppender etc.
Note: this implementation is not thread-safe.
A bit simpler way is to use Enum which is singleton by default and can implement interfaces.
public enum Appender implements ConditionAppender {
AND(" AND "), OR(" OR ");
final String operation;
Appender(String operation) {
this.operation = operation;
}
#Override
public String append() {
return operation;
}
public static void main(String[] args) {
System.out.println(AND.append());
System.out.println(OR.append());
}
}
The code below may help you~
public abstract class AbstractSingleton {
private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();
AbstractSingleton() throws SingletonException {
String clazzName = this.getClass().getName();
if (registryMap.containsKey(clazzName)) {
throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
} else {
synchronized (registryMap) {
if (registryMap.containsKey(clazzName)) {
throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
} else {
registryMap.put(clazzName, this);
}
}
}
}
#SuppressWarnings("unchecked")
public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
String clazzName = clazz.getName();
if (!registryMap.containsKey(clazzName)) {
synchronized (registryMap) {
if (!registryMap.containsKey(clazzName)) {
T instance = clazz.newInstance();
return instance;
}
}
}
return (T) registryMap.get(clazzName);
}
public static AbstractSingleton getInstance(final String clazzName)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
if (!registryMap.containsKey(clazzName)) {
Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
synchronized (registryMap) {
if (!registryMap.containsKey(clazzName)) {
AbstractSingleton instance = clazz.newInstance();
return instance;
}
}
}
return registryMap.get(clazzName);
}
#SuppressWarnings("unchecked")
public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
throws SecurityException, NoSuchMethodException, IllegalArgumentException,
InvocationTargetException, InstantiationException, IllegalAccessException {
String clazzName = clazz.getName();
if (!registryMap.containsKey(clazzName)) {
synchronized (registryMap) {
if (!registryMap.containsKey(clazzName)) {
Constructor<T> constructor = clazz.getConstructor(parameterTypes);
T instance = constructor.newInstance(initargs);
return instance;
}
}
}
return (T) registryMap.get(clazzName);
}
static class SingletonException extends Exception {
private static final long serialVersionUID = -8633183690442262445L;
private SingletonException(String message) {
super(message);
}
}
}
From: https://www.cnblogs.com/wang9192/p/3975748.html
I have below code. As you can see I am executing similar logic but once for Bike and once for Car. Can I make use for <K> to reduce duplicate looking code? I have not used <K> so I am not sure where and how exactly I can incorporate it. Where can I make the decision whether to call getCarsWithFeature or getBikesWithFeature?
Is it best practice to reduce number of lines (may make it less readable) or to have such duplicate-looking code?
public Set<Car> getPremiumCars(String filter) {
final Callable<Set<Car>> retryGetCars = new RetryingCallable<>(retryStrategy(), getCars(filter));
return retryGetCars.call();
}
public Callable<Set<Car>> getCars(String feature) {
return new Callable<Set<Car>>() {
#Override
public Set<Car> call() throws Exception {
Set<Car> cars = getCarsWithFeature(feature);
return Collections.unmodifiableSet(cars);
}
};
}
public Set<Bike> getPremiumBikes(String filter) {
final Callable<Set<Bike>> retryGetBikes = new RetryingCallable<>(retryStrategy(), getBikes(filter));
return retryGetBikes.call();
}
public Callable<Set<Bike>> getBikes(String feature) {
return new Callable<Set<Bike>>() {
#Override
public Set<Bike> call() throws Exception {
Set<Bike> bikes = getBikesWithFeature(feature);
return Collections.unmodifiableSet(bikes);
}
};
}
I do not know your whole code, but I would suggest for both classes to implement same interface - let's say Vehicle:
public interface Vehicle {
}
Then you could write code that lately you can reuse:
public <T extends Vehicle> Set<T> getPremiumVehicle(Function<String, Callable<Set<T>>> vehicleSupplier, String filter) throws Exception {
final Callable<Set<T>> retryGetCars = new RetryingCallable<T>(retryStrategy(), vehicleSupplier.apply(filter));
return retryGetCars.call();
}
public <T extends Vehicle> Callable<Set<T>> getVehicle(Function<String, Set<T>> vehicleSupplier, String feature) {
return () -> {
Set<T> vehicles = vehicleSupplier.apply(feature);
return Collections.unmodifiableSet(vehicles);
};
}
Now, you could reuse above code, like:
public Set<Car> getPremiumCars(String filter) throws Exception {
return getPremiumVehicle(this::getCars, filter);
}
public Set<Bike> getPremiumBikes(String filter) throws Exception {
return getPremiumVehicle(this::getBikes, filter);
}
public Callable<Set<Car>> getCars(String feature) {
return getVehicle(this::getCarsWithFeature, feature);
}
public Callable<Set<Bike>> getBikes(String feature) {
return getVehicle(this::getBikesWithFeature, feature);
}
Create a base class of Car and Bike, then put the common method there.
Then extend the Car and Bike from it. Update the common method using the base class. Sample hints for the implementation is given below:
class Vehicle {
public Set<Vehicle> getWithFilter(String filter) {
final Callable<Set<Vehicle>> retryGet = new RetryingCallable<>(retryStrategy(), get(filter));
return retryGet.call();
}
public Callable<Set<Vehicle>> getWithFeature(String feature) {
return new Callable<Set<Vehicle>>() {
public Set<Vehicle> call() throws Exception {
Set<Vehicle> vehicles = getWithFeature(feature);
return Collections.unmodifiableSet(vehicles);
}
};
}
}
class Car extends Vehicle {
}
class Bike extends Vehicle {
}
Now to call use:
Car car = new Car();
car.getWithFilter(/* Pass parameter*/);
Bike bike = new Bike();
bike.getWithFilter(/* Pass parameter*/);
I now came several times across this problem and always kinda solved this with some casts and #SuppressWarnings annotations.
The relevant interfaces / abstract classes:
public abstract class Data { }
public interface DataOperations {
boolean isValid();
}
public interface DataOperationsFactory<T extends Data> {
Class<T> getDataClass();
DataOperations getOperations(T data);
}
Example Implementations:
public class DataImpl1 extends Data {
public String foo;
}
public class DataImpl1Operations implements DataOperations {
private DataImpl1 data;
public DataImpl1Operations(DataImpl1 data) {
this.data = data;
}
public boolean isValid() {
return data.foo != null;
}
}
public class DataImpl1OperationsFactory extends DataOperationsFactory<DataImpl1> {
public Class<DataImpl1> getDataClass() {
return DataImpl1.class;
}
DataOperations getOperations(DataImpl1 data) {
return new DataImpl1Operations(data);
}
}
Using this pattern, I can decide whether I need to create a new DataImpl1Operations everytime. Or maybe use a final static NO_OP implementation or what have you.
The Code:
Now I'd like to put all those factories inside a Map<Class<T>, DataOperationsFactory<T>> (constructor). And afterwards read from it (getOps method).
public class Test {
Map<Class<?>, DataOperationsFactory<?>> map;
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
#SuppressWarnings("unchecked")
public <T extends Data> DataOperations getOps(T data) {
// --> Here I need to do an unchecked cast <--
DataOperationsFactory<? super T> f =
(DataOperationsFactory<? super T>) map.get(data.getClass());
return f.getOperations(data);
}
}
Is there any way doing this without unchecked casting?
You can delegate to a private method that captures the type, so it can be used to reliably cast to the correct Data subclass:
Map<Class<?>, DataOperationsFactory<?>> map;
// Unchanged
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
public DataOperations getOps(Data data) {
DataOperationsFactory<?> f = map.get(data.getClass());
return getOperations(f, data);
}
private static <T extends Data> DataOperations getOperations(DataOperationsFactory<T> f,
Data data) {
return f.getOperations(f.getDataClass().cast(data));
}
I have a generic interface Handler
public interface Handler<T> {
void handle(T obj);
}
I can have n implementations of this interface. Let's say I have following 2 implementations for now. One which handles String objects and another handles Date
public class StringHandler implements Handler<String> {
#Override
public void handle(String str) {
System.out.println(str);
}
}
public class DateHandler implements Handler<Date> {
#Override
public void handle(Date date) {
System.out.println(date);
}
}
I want to write a factory which will return handler instances based on the class type. Something like this :
class HandlerFactory {
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz == String.class) return new StringHandler();
if (clazz == Date.class) return new DateHandler();
}
}
I get following error in this factory :
Type mismatch: cannot convert from StringHandler to Handler<T>
How to fix this?
SIMPLE SOLUTION
You could save your mappings Class<T> -> Handler<T> in a Map. Something like:
Map<Class<T>, Handler<T>> registry = new HashMap<>();
public void registerHandler(Class<T> dataType, Class<? extends Handler> handlerType) {
registry.put(dataType, handlerType);
}
public <T> Handler<T> getHandler(Class<T> clazz) {
return registry.get(clazz).newInstance();
}
In some place, initialize handlers (could be in the factory itself):
factory.registerHandler(String.class, StringHandler.class);
factory.registerHandler(Date.class, DateHandler.class);
And in another place, you create and use them:
Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
MORE COMPLEX SOLUTION
You can "scan" classes using reflection and, instead of register manually the mappings Class<T> -> Handler<T>, do it using reflection.
for (Class<? extends Handler> handlerType : getHandlerClasses()) {
Type[] implementedInterfaces = handlerType.getGenericInterfaces();
ParameterizedType eventHandlerInterface = (ParameterizedType) implementedInterfaces[0];
Type[] types = eventHandlerInterface.getActualTypeArguments();
Class dataType = (Class) types[0]; // <--String or Date, in your case
factory.registerHandler(dataType, handlerType);
}
Then, you create and use them like above:
Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
To implement getHandlerClasses(), look at this to scan all classes in your jar. For each class, you have to check if it is a Handler:
if (Handler.class.isAssignableFrom(scanningClazz) //implements Handler
&& scanningClazz.getName() != Handler.class.getName()) //it is not Handler.class itself
{
//is a handler!
}
Hope it helps!
Your problem is that the compiler cannot make the leap to the fact thet the type of the result is correct.
To help the compiler you can make the factory delegate the construction. Although this looks strange and unwieldly it does manage to properly maintain type safety without sacrifices such as casting or using ? or raw types.
public interface Handler<T> {
void handle(T obj);
}
public static class StringHandler implements Handler<String> {
#Override
public void handle(String str) {
System.out.println(str);
}
}
public static class DateHandler implements Handler<Date> {
#Override
public void handle(Date date) {
System.out.println(date);
}
}
static class HandlerFactory {
enum ValidHandler {
String {
#Override
Handler<String> make() {
return new StringHandler();
}
},
Date {
#Override
Handler<Date> make() {
return new DateHandler();
}
};
abstract <T> Handler<T> make();
}
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz == String.class) {
return ValidHandler.String.make();
}
if (clazz == Date.class) {
return ValidHandler.Date.make();
}
return null;
}
}
public void test() {
HandlerFactory factory = new HandlerFactory();
Handler<String> stringHandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
}
The whole point of using a generic type is to share the implementation. If the n implementation of your Handler interface are so different that they can't be shared, then I don't think there is any reason to use define that generic interface at the first place. You'd rather just have StringHandler and DateHandler as top level classes.
On the other hand, if the implementation can be shared, as is the case of your example, then the factory works naturally:
public class Main {
static public interface Handler<T> {
void handle(T obj);
}
static public class PrintHandler<T> implements Handler<T> {
#Override
public void handle(T obj) {
System.out.println(obj);
}
}
static class HandlerFactory {
public static <T> Handler<T> getHandler() {
return new PrintHandler<T>();
}
}
public static void main(String[] args) {
Handler<String> stringHandler = HandlerFactory.getHandler();
Handler<Date> dateHandler = HandlerFactory.getHandler();
stringHandler.handle("TEST");
dateHandler.handle(new Date());
}
}
You can use something like:
class HandlerFactory {
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz.equals(String.class)) return (Handler<T>) new StringHandler();
if (clazz.equals(Date.class)) return (Handler<T>) new DateHandler();
return null;
}
}
T is generic and the compiler can't map that at compile time. Also it is safer to use .equals instead of ==.
Define an interface for creating an object, but let subclasses decide which class to instantiate.
Factory method lets a class defer instantiation to subclasses.
Define generic abstract class
public abstract class Factory<T> {
public abstract T instantiate(Supplier<? extends T> supplier);
}
And a generic supplier
public class SupplierFactory<T> extends Factory<T> {
#Override
public T instantiate(Supplier<? extends T> supplier) {
return supplier.get();
}
}
Then an implementation needs to have concrete classes to implement the base interface and a main class to show class defer instantiation . i.e
The base interface (desired interface of the requirement)
public interface BaseInterface {
void doAction();
}
The first concrete class
public class Alpha implements BaseInterface {
#Override
public void doAction() {
System.out.println("The Alpha executed");
}
}
And the second one
public class Beta implements BaseInterface {
#Override
public void doAction() {
System.out.println("The Beta executed");
}
}
The main
public class Main {
public static void main(String[] args) {
Factory<BaseInterface> secondFactory = new SupplierFactory<>();
secondFactory.instantiate(Beta::new).doAction();
secondFactory.instantiate(Alpha::new).doAction();
}
}
Basically you can do:
public Handler getHandler( Class clazz ){
if( clazz == String.class ) return new StringHandler();
if( clazz == Date.class ) return new DateHandler();
return null;
}
public static void main( String[] args ){
HandlerFactory handlerFactory = new HandlerFactory();
StringHandler handler = ( StringHandler )handlerFactory.getHandler( String.class );
handler.handle( "TEST" );
DateHandler handler2 = ( DateHandler )handlerFactory.getHandler( Date.class );
handler2.handle( new Date() );
}
Output:
TEST
Tue Dec 15 15:31:00 CET 2015
But instead writing two different methods to get handlers separately always is a better way.
I edited your code and allowed Eclipse to "fix" the errors and it came up with this.
public Handler<?> getHandler(Class<?> clazz) {
if (clazz == String.class)
return new StringHandler();
if (clazz == Date.class)
return new DateHandler();
return null;
}
Yout HandlerFactory don't know about T. Use your factory like below-
public class HandlerFactory {
public Handler<?> getHandler(Class<?> clazz) {
if (clazz == String.class) {
return new StringHandler();
}
if (clazz == Date.class) {
return new DateHandler();
}
return null;
}
}
I have an third-party RPC-API that provides an interface similar to that of java.sql.ResultSet (for reading values) and java.sql.PreparedStatement (for writing values). Assume it looks something like this:
public interface RemoteDeviceProxy {
public void setBoolean(Boolean value);
public void setInteger(Integer value);
// ...
public Boolean getBoolean();
public Integer getInteger();
// ...
}
I want to write a wrapper for this API that uses generics to create instances of specific types:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
public RemoteVariable(RemoteDeviceProxy wrappedDevice) {
this.wrappedDevice = wrappedDevice;
}
public T get() {
// should call wrappedDevice.getBoolean() if T is Boolean, etc.
// how to implement?
}
public void set(T newValue) {
// should call wrappedDevice.setBoolean(newValue) if T is Boolean, etc.
// implement using instanceof
}
}
How can I implement the getter in my generic wrapper? I have found this answer which explains a similar scenario in depth, but I am not able to transfer this to my problem. Specifically, when I write this:
public T get() {
Type[] actualTypeArguments = ((ParameterizedType) getClass())
.getActualTypeArguments();
}
I get a compiler error saying I cannot cast to ParameterizedType, and I do not understand why. Can anyone explain how to achieve this?
Here is one way:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
private final Class<T> clazz;
public RemoteVariable(RemoteDeviceProxy wrappedDevice, Class<T> clazz) {
this.wrappedDevice = wrappedDevice;
this.clazz = clazz;
}
public T get() {
if(clazz == Boolean.class){return clazz.cast(wrappedDevice.getBoolean());}
else if(clazz == Integer.class){return clazz.cast(wrappedDevice.getInteger());}
// ...
}
// ...
}
I thought over this quite a while and finally came up with a different approach:
First I added a getter to you RemoteVariable class:
protected RemoteDeviceProxy getWrappedProxy() {
return wrappedProxy;
}
Second I created a builder interface that will be used by a factory later:
public interface RemoteVariableBuilder {
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy);
}
Then I created non generic sub classes for Boolean...
public class RemoteBooleanVariable extends RemoteVariable<Boolean> implements RemoteVariableBuilder {
public RemoteBooleanVariable(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteBooleanVariable(wrappedProxy);
}
#Override
public Boolean get() {
return getWrappedProxy().getBoolean();
}
#Override
public void set(Boolean value) {
getWrappedProxy().setBoolean(value);
}
}
... and Integer ...
public class RemoteIntegerBuilder extends RemoteVariable<Integer> implements RemoteVariableBuilder {
public RemoteIntegerBuilder(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteIntegerBuilder(wrappedProxy);
}
#Override
public Integer get() {
return getWrappedProxy().getInteger();
}
#Override
public void set(Integer value) {
getWrappedProxy().setInteger(value);
}
}
actually eclipse created most of the code once it knew base class and interface.
The final step was to create a factory
public class RemoteVariableFactory {
private static final Map<String, RemoteVariableBuilder> BUILDERS = new HashMap<>();
static {
BUILDERS.put(Boolean.class.getName(), new RemoteBooleanVariable(null));
BUILDERS.put(Integer.class.getName(), new RemoteIntegerBuilder(null));
// add more builders here
}
public static <T> RemoteVariable<T> getRemoteVariable(RemoteDeviceProxy wrappedProxy, Class<T> typeClass) {
RemoteVariableBuilder remoteVariableBuilder = BUILDERS.get(typeClass.getName());
if (remoteVariableBuilder == null) {
return null; // or throw an exception whichever is better in your case
}
return remoteVariableBuilder.buildNewVariable(wrappedProxy);
}
}
Now we are ready to create new RemoteVariables...
RemoteVariable<Boolean> var1 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Boolean.class);
RemoteVariable<Integer> var2 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Integer.class);
To conclude this let's do a quick comparison to the answer of Eng.Fouad:
Disadvantage:
you need to create a new class for every datatype you provide
Advantage:
you only have to add one line to the static block of the factory and not two new if blocks to the getter and setter in RemoteVariable
get and set do not have to work through the if-else-blocks every time