Factory Pattern with Static Factory method or Constructor - java

I have a hierarchy of classes RequestParams , all can be created with ExtArgs.
public interface RequestParams {
}
I also created a Factory RequestsFactory looking like this:
public class RequestFactory<P extends RequestParams> {
private final Logger logger = LoggerFactory.getLogger(RequestFactory.class);
final Class<P> paramsClass;
public RequestFactory(Class<P> paramsClass) {
this.paramsClass = paramsClass;
}
public Request<P> create(ExternalArgs args) {
P params = null;
try {
params = paramsClass.getDeclaredConstructor(ExternalArgs.getClass()).newInstance(args);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
logger.error("Failed to generate request", e);
}
return new Request<>("get", Collections.singletonList(params));
}
static <P extends RequestParams> RequestFactory<P> createFactory(
final Class<P> paramsClass) {
return new RequestFactory<P>(paramsClass);
}
}
where my client code is s follows:
Request devInfoRequest = RequestFactory.createFactory(RequestType.SYSTEM_INFO.getRequestClass()).create(externalArgs);
I use this ENUM in order map request types:
public enum RequestType {
SYSTEM_INFO(DeviceInfoParams.class),
INTERFACES(InterfacesParams.class)
}
My question is - How can use this, since I can't add neither constructor nor static builder to the RequestParams interface (or abstract class) ?
EDIT: (After AdamSkywalker's answer):
I want my client code to be:
Request<InterfacesParams> interfacesRequest =
RequestFactory.<InterfacesParams>createFactory(RequestType.INTERFACES.create(externalArgs)).create("get");
But it requires the enum return function to match the generic type.

As you've observed, the problem with the built-in enum feature is that every enum-value has to have the same type; they can't have different type arguments (in fact, the enum-class won't even accept a type parameter).
One option is to roll your own enumeration — just write a class RequestType<P extends RequestParam> with a private constructor and a normal static field for each instance.
But if the RequestType and RequestFactory<...> types really only have what you've shown here, then it probably makes more sense to just merge them, by writing:
public final class RequestFactory<P extends RequestParams> {
public static final RequestFactory<DeviceInfoParams> SYSTEM_INFO =
new RequestFactory<>(DeviceInfoParams::new);
public static final RequestFactory<InterfacesParams> INTERFACES =
new RequestFactory<>(InterfacesParams::new);
private final Function<ExternalArgs, P> mRequestParamsCreator;
private RequestFactory(final Function<ExternalArgs, P> requestParamsCreator) {
mRequestParamsCreator = requestParamsCreator;
}
public Request<P> create(final ExternalArgs externalArgs) {
final P requestParams = mRequestParamsCreator.apply(externalArgs);
return new Request<P>("get", Collections.singletonList(requestParams));
}
}
(Note: the above uses some Java 8 / JDK 1.8 features for conciseness. If you're still using an older version of Java, the above approach will still work, but you'll have to write a bit of boilerplate to fill in the gaps. Likewise if the relevant constructors of DeviceInfoParams and InterfacesParams declare any checked exceptions, since in that case they can't be implicitly converted to java.util.function.Functions.)
You can then create requests by writing (for example):
final Request<DeviceInfoParams> systemInfoRequest =
RequestFactory.SYSTEM_INFO.create(externalArgs);
final Request<InterfacesParams> interfacesRequest =
RequestFactory.INTERFACES.create(externalArgs);
As with AdamSkywalker's approach, this avoids reflection completely.

Maybe it's not exactly what you're looking for, but your code is so over engineered. Why not:
public enum RequestType {
SYSTEM_INFO {
public RequestParams create(ExternalArgs args) { return new DeviceInfoParams(args); }
},
INTERFACES {
public RequestParams create(ExternalArgs args) { return new InterfacesParams(args); }
};
abstract RequestParams create(ExternalArgs args);
}
And then call it without reflection tricks:
Request req = new Request("get", singletonList(SYSTEM_INFO.create(args)));
You may still use your factory, but the point is to remove the reflection complexity.

Related

How do I strip annotations from local variables in Java annotation processors?

Due to a specific reason, I would like to use Checker Framework and its subtyping checker.
To make this checker work I have to use ElementType.TYPE_PARAMETER and ElementType.TYPE_USE.
However, I would like to remove them from local variables before compilation to class files.
For example, let's say I have the following code with custom #FirstName and #LastName (both must retain at the class level with RetentionPolicy.CLASS):
#FirstName String firstName = ...;
#LastName String lastName = ...;
...
firstName = lastName; // illegal, the error is generated by Checker Framework because the first name cannot be assigned to the last name
but for another reason, I would like to remove the annotations from the local variables "at" bytecode level as if the source code is just:
String firstName = ...;
String lastName = ...;
...
firstName = lastName; // totally fine and legal in Java
If I understand the way it can be accomplished, annotation processing is a way to go.
So, if it's a right thing to do, then I'd have to chain some annotation processors in the following order:
org.checkerframework.common.subtyping.SubtypingChecker.
my custom "remove local variables annotations" annotation processor;
Well, diving into how javac works is an extreme challenge to me.
What I have implemented so far is:
#SupportedOptions(RemoveLocalVariableAnnotationsProcessor.ANNOTATIONS_OPTION)
#SupportedAnnotationTypes("*")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public final class RemoveLocalVariableAnnotationsProcessor
extends AbstractProcessor {
private static final Pattern commaPattern = Pattern.compile(",");
static final String ANNOTATIONS_OPTION = "RemoveLocalVariableAnnotationsProcessor.annotations";
#Nonnull
private Predicate<? super Class<? extends Annotation>> annotationClasses = clazz -> false;
#Override
public void init(#Nonnull final ProcessingEnvironment environment) {
super.init(environment);
final Messager messager = environment.getMessager();
final Map<String, String> options = environment.getOptions();
#Nullable
final String annotationsOption = options.get(ANNOTATIONS_OPTION);
if ( annotationsOption != null ) {
annotationClasses = commaPattern.splitAsStream(annotationsOption)
.<Class<? extends Annotation>>flatMap(className -> {
try {
#SuppressWarnings("unchecked")
final Class<? extends Annotation> clazz = (Class<? extends Annotation>) Class.forName(className);
if ( !clazz.isAnnotation() ) {
messager.printMessage(Diagnostic.Kind.WARNING, "Not an annotation: " + className);
return Stream.empty();
}
return Stream.of(clazz);
} catch ( final ClassNotFoundException ex ) {
messager.printMessage(Diagnostic.Kind.WARNING, "Cannot find " + className);
return Stream.empty();
}
})
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet))
::contains;
}
final Trees trees = Trees.instance(environment);
final JavacTask javacTask = JavacTask.instance(environment);
javacTask.addTaskListener(new RemoverTaskListener(trees, messager));
}
#Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment environment) {
// do nothing: ElementType.TYPE_USE and ElementType.TYPE_PARAMETER seem to be unable to be analyzed here
return false;
}
private static final class RemoverTaskListener
implements TaskListener {
private final Trees trees;
private final Messager messager;
private RemoverTaskListener(final Trees trees, final Messager messager) {
this.trees = trees;
this.messager = messager;
}
#Override
public void started(final TaskEvent taskEvent) {
if ( taskEvent.getKind() == TaskEvent.Kind.ANALYZE ) {
final TreeScanner<?, ?> remover = new Remover(trees, messager);
remover.scan(taskEvent.getCompilationUnit(), null);
}
}
#Override
public void finished(final TaskEvent taskEvent) {
// do nothing
}
private static final class Remover
extends TreePathScanner<Void, Void> {
private final Trees trees;
private final Messager messager;
private Remover(final Trees trees, final Messager messager) {
this.trees = trees;
this.messager = messager;
}
#Override
public Void visitVariable(final VariableTree variableTree, final Void nothing) {
super.visitVariable(variableTree, nothing);
final Symbol symbol = (Symbol) trees.getElement(trees.getPath(getCurrentPath().getCompilationUnit(), variableTree));
if ( !symbol.hasTypeAnnotations() || symbol.getKind() != ElementKind.LOCAL_VARIABLE ) {
return nothing;
}
final List<? extends AnnotationTree> annotationTrees = variableTree.getModifiers().getAnnotations();
if ( annotationTrees.isEmpty() ) {
return nothing;
}
messager.printMessage(Diagnostic.Kind.WARNING, "TODO: " + symbol);
for ( final AnnotationTree annotationTree : annotationTrees ) {
// TODO how to align AnnotationTree and java.lang.annotation.Annotation?
// TODO how to remove the annotation from the local variable?
}
return nothing;
}
}
}
}
As you can see, it does not work as it's supposed to do.
What is a proper way of removing the annotations from local variables?
I mean, how do I accomplish it?
If it's possible, I would like to stick to javac annotation processors due to the Maven build integration specifics.
As far as I know, you can't do it this way:
javac annotation processors (JSR-269) can't modify code. They can only observe it and generate new code that will be compiled together with the hand-written code. Thus, annotation processing is done in multiple rounds to allow compiler and other annotation processors see the newly generated code. The processing stops basically when no new code is generated at the end of the round.
This way the order of annotation processor invocations is not defined, and that's okay, because of multi-round compilation - it helps solving cyclic dependencies.
What you need is a bytecode rewriter (ASM library would do well). Such tools operate on resulting .class files after compilation is done. Yet again, AFAIK, annotation processing is embedded into compilation itself, so you won't be able to rewrite bytecode before Checker annotation processor sees it.
So, sadly, I don't see any solution, but to try and fork the Checker Framework and make it ignore annotations you want, if of course it doesn't already have options to turn certain validations off.

Replace a reflective call with a dynamically generated class

I have an interface similar to this:
public interface Getter {
Object get(Params params);
}
that I implement using a reflective call to a different method:
public class GetterImpl implements Getter {
private final Object target;
private final Method method; //doStuff method
public GetterImpl(Object target, Method method) {
this.target = target;
this.method = method;
}
#Override
public Object get(Params params) {
//both the target and arguments depend on Params
return method.invoke(chooseTarget(params), prepareArgs(params));
}
private Object chooseTarget(Params params) {
if (params.getTargetOverride() != null) {
return params.getTargetOverride();
}
return target;
}
private Object[] prepareArgs(Params params) {
...
}
}
Is it possible to instead generate a class implementing Getter with equivalent logic but without reflection? Effectively a class like this:
public class GeneratedGetterImpl implements Getter {
...
#Override
public Object get(Params params) {
//somehow call doStuff directly (different method for each generated impl)
return target.doStuff(prepareArgs(params));
}
}
I'm looking into using Byte Buddy to generate such a class on the fly, but all the examples provide some sort of statically known method interceptor, and never delegate to a dynamically chosen target and method.
It's clearly not a trivial task, but can this be done with Byte Buddy? Or a different library?
UPDATE:
Here's my best attempt so far:
Target target = new Target();
Method method = Target.class.getMethod("doStuff", Book.class);
//Helper class that computes the new arguments based on the original
Prepare prepare = new Prepare();
Method doPrep = Prepare.class.getMethod("doPrep", Params.class);
Getter getter = (Getter) new ByteBuddy()
.subclass(Object.class)
.implement(Getter.class)
.method(named("get")).intercept(
MethodCall.invoke(method).on(target)
.withMethodCall(
MethodCall.invoke(doPrep).on(prepare).withAllArguments()
))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance();
public static class Prepare {
public Book doPrep(Params params) {
return new Book(params.getTitle());
}
}
This does what I want, but only if the targeted method takes 1 argument (Book in my case). I'm struggling to figure out how to have it return an array that I then spread when calling the target method.
E.g.
public static class Prepare {
//returns all the arguments
public Object[] doPrep(Params params) {
return new Object[] { new Book(params.getTitle()) };
}
}
Such a facility does already exists in the JRE, if we restrict it to binding an interface to a matching target method.
public static void main(String[] args) throws NoSuchMethodException {
Function<Double,Double> f1 = create(Math.class.getMethod("abs", double.class));
System.out.println(f1.apply(-42.0));
Map<Double,Double> m = new HashMap<>();
Function<Double,Double> f2 = create(Map.class.getMethod("get", Object.class), m);
m.put(1.0, 123.0);
System.out.println(f2.apply(1.0));
}
static Function<Double,Double> create(Method m) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class), t.erase(), l.unreflect(m), t)
.getTarget().invoke();
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
static Function<Double,Double> create(Method m, Object target) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class, m.getDeclaringClass()),
t.erase(), l.unreflect(m), t)
.getTarget().invoke(target);
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
42.0
123.0
This demonstrates that adaptations like auto-boxing and casting as required for generic functions are included, but any other adaptations of parameters or results are not possible and have to be performed by pre-existing decorating code. Most notably, varargs processing is not included.
The documentation is exhaustive. It’s strongly recommended to read it in all details before using the class. But the things you can do wrong here, are similar to the things you can do wrong when implementing your own bytecode generator.
Using Byte Buddy, you can create a MethodCall instance that represents your proxied method and use it as an implementation. I assume that you looked into delegation which requires a more static model:
MethodCall.invoke(SomeClass.class.getMethod("foo")).with(...)
You can also provide other method call instances as arguments to methods to achieve what you have in your example code.
As for your updated question, I'd recommend you a hybrid approach. Implement some container:
class Builder<T> {
Builder with<T>(T value);
T[] toArray();
}
and then you can use Byte Buddy to invoke it for creating your result value:
MethodCall builder = MethodCall.construct(Builder.class.getConstructor());
for (SomeInfoObject info : ...) {
builder = MethodCall.invoke(Builder.class.getMethod("with", Object.class))
.on(builder)
.with(toMethodCall(info));
}
builder = MethodCall.invoke(Builder.class.getMethod("toArray")).on(builder);
Byte Buddy's goal is to make weaving code easy, not to replace writing static code which is the much better option if you have the opportunity.

Make code more Generic in Java

I have a Trigger Manager scenario where I delegate the triggers (in other-words subscribe triggers) to different handlers.
For now I have three handler types, I use a switch-case with enum (enum here is the handler type) to redirect to correct handler.
But my code seems not extensible, its not generic and it doesn't follow SOLID principle. Imagine if I need to have more handler
I will be eventually coming and editing my switch case code and I will have more cases where it affects the cyclomatic complexity of my code
Below is my exact code snippet
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException {
switch (triggerHandlerType) {
case DASHBOARD_HANDLER:
triggerContext = new TriggerContext(new DashboardTriggerHandler());
return triggerContext;
case COMPONENT_HANDLER:
triggerContext = new TriggerContext(new ComponentTriggerHandler());
return triggerContext;
case WIDGET_HANDLER:
triggerContext = new TriggerContext(new WidgetTriggerHandler());
return triggerContext;
default:
LOGGER.error(MIS_CONFIGURED_REQUEST_IS_PROVIDED);
throw new TriggerHandlerException(TRIGGER_HANDLER_TYPE_GIVEN_IS_NOT_CONFIGURED_IN_THE_LIST_OF_TRIGGER_HANDLERS);
}
}
Can someone help me to enhance this code in-which I can make it more generic and avoid cyclomatic complexity and follow SOLID Principle along with some design pattern.
I think you mean "make code more dynamic", and your problem comes from using objects as primitives.
Rather than switching on the enum object, your enum objects should contain the type to be instantiated:
enum TriggerHandlerType {
DASHBOARD {
#Override
TriggerHandler create() {
return new DashboardTriggerHandler();
}
},
COMPONENT_HANDLER {
//...
};
abstract TriggerHandler create();
}
getTriggerContext can then call create() to instantiate the handler:
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException {
return new TriggerContext(triggerHandlerType.create());
}
I am not sure about the overall design structure, but the switch can be replaced with a newHandler() method on the enum.
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType)
throws TriggerHandlerException
{
return new TriggerContext(triggerHandlerType.newHandler());
}
In the enum you would implement the method for each type enum as
enum TriggerHandlerType {
DASHBOARD_HANDLER
{
Handler newHandler() { return new DashboardHandler(); }
},
...;
abstract Handler newHandler();
}
You could use a map of configurations for that:
// All your triggers classes should implement this interface
interface TriggerHandler {}
// For example:
public static class DashboardTriggerHandler implements TriggerHandler {
}
// Create your configuration
static Map<TriggerHandlerType, Class> contexts;
static {
contexts = new HashMap<>();
contexts.put(TriggerHandlerType.DASHBOARD_HANDLER, DashboardTriggerHandler.class);
contexts.put(TriggerHandlerType.COMPONENT_HANDLER, ComponentTriggerHandler.class);
contexts.put(TriggerHandlerType.WIDGET_HANDLER, WidgetTriggerHandler.class);
}
// Return your instance through reflection
public static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException, IllegalAccessException, InstantiationException {
Class className = contexts.get(triggerHandlerType);
if (className == null) {
throw new TriggerHandlerException();
}
return new TriggerContext((TriggerHandler)className.newInstance());
}

Generic type invocation using string class names

Hope you can help me with this:
I have ...
a string list of class names called classNameList
a generic class Geography<T>
a static generic method <T> void read(Class<T> cl, Geography<T> geo)
I want to loop through the string class name list and call the generic method for each of these classes.
What I tried but obviously did not work:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Error: myClass.newInstance cannot be resolved to a type
My code runs perfectly for a single call of the generic function:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Any ideas how I could do this?
UPDATE:
Thanks for the helpful input, still it's hard for me to adopt it to my real code.
So this is the non simplyfied problem:
I do ready in shapefile-Data with a shapefileLoader, for each feature of the Shapefile a class (GuadAgent) is initialized with a predifined class (PlantWind). I have shapefiles in my input-directory with the names of the Classes their features do represent. I want Java to read in the shapefiles and create the respective agent class. (the agents are also placed in a context and a geography..)
Used classes are: ShapefileLoader, Geography, the other classes can be find at the same website
This part is in the main-method:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The static Method that reads in the names:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
How can I get rid of the switch? Although their number is finit, there are very many different agents...
Unfortunately, there's no syntax close to your intention (nice idea though).
The basic problem is that Class.forName() returns an unknown Class<?>, so you need a cast somewhere. It's just a mater of where you put it.
I suggest this approach (which compiles) that bundles up doing a read() based on a class name:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
May I also suggest using the foreach loop syntax, for tidier code:
for (String className : classNameList) {
readForName(className.getName());
}
Creating instances from Generic Types at Runtime
I am not entirely clear on what you are trying to accomplish, but at first look it looks like the simplest solution is the best solution.
It could be solved with using a scripting environment ( Groovy, JavaScript, JRuby, Jython ) that could dynamically evaluate and execute arbitrary code to create the objects, but that got extremely convoluted and overly complex, just to create an object.
But unfortunately I think it has a very pedestrian solution.
As long as there is a predefined set of supported types, you can use a Factory pattern. Here I just leverage the Provider<>T interface from the javax.inject/com.google.inject package.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(#Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
#Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Disclaimer:
This is just a proof of concept example, I would never use a switch
statement like that in production code I would use a Strategy
Pattern or Chain of Responsibility Pattern to encapsulate the logic
of what type to create based on the ClassName key.
This initially looked like a generics problem, it isn't, it is a creation problem.
That said, you don't need to pass around instances of Class<?> you can get Generic Type information off of Parameterized classes at runtime with TypeToken from Guava.
You can even create instances of any generic type at runtime with TypeToken from the Guava library.
The main problem is this syntax isn't supported: Geography<myClass.newInstance()> geo; and I can't think of anyway to fake it other than the Provider implementation above.
Here is a straw man example of how to use TypeToken so that your
parameterized classes will always know their types!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Notes:
Works great for classes that have a default no arg constructor.
Works better than using straight reflection if there are no default no arg constructors.
Should play well with Guice allowing you to use the ".getRawType()generatedClassto pass togetInstance()` of an Injector. have not tried this yet, I just thought of it!
You can use Class<T>.cast() to do casting that doesn't need #SuppressWarning("unchecked") all over the place.`
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
Update: I'm not sure what the Geography class is meant to do. If it needs a generically typed object, it might look like this:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
If it needs a class, the constructor might look like this:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}

How to convert one enum to another enum in java?

I have;
public enum Detailed {
PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}
and need to convert it to the following;
public enum Simple {
DONE, RUNNING, ERROR;
}
So first PASSED->DONE and INPROCESS->RUNNING, but all errors should be: ERROR. Obviously it is possible to write cases for all values, but there may be a better solution?
Personally I would just create a Map<Detailed, Simple> and do it explicitly - or even use a switch statement, potentially.
Another alternative would be to pass the mapping into the constructor - you could only do it one way round, of course:
public enum Detailed {
PASSED(Simple.DONE),
INPROCESS(Simple.RUNNING),
ERROR1(Simple.ERROR),
ERROR2(Simple.ERROR),
ERROR3(Simple.ERROR);
private final Simple simple;
private Detailed(Simple simple) {
this.simple = simple;
}
public Simple toSimple() {
return simple;
}
}
(I find this simpler than Ted's approach of using polymorphism, as we're not really trying to provide different behaviour - just a different simple mapping.)
While you could potentially do something cunning with the ordinal value, it would be much less obvious, and take more code - I don't think there'd be any benefit.
One way is to define a method asSimple() in your Detailed enum:
public enum Detailed {
PASSED {
#Override
Simple asSimple() {
return DONE;
}
},
INPROCESS {
#Override
Simple asSimple() {
return RUNNING;
}
},
ERROR1,
ERROR2,
ERROR3;
public Simple asSimple() {
return Simple.ERROR; // default mapping
}
}
You can then simply call the method when you want to do the mapping:
Detailed code = . . .
Simple simpleCode = code.asSimple();
It has the advantage of putting the knowledge of the mapping with the Detailed enum (where perhaps it belongs). It has the disadvantage of having knowledge of Simple mixed in with the code for Detailed. This may or may not be a bad thing, depending on your system architecture.
Use EnumMap
I decouple my external xml interface from my internal domain model by implementing a transformation service. This includes mapping enums from jaxb generated code to domain model enums.
Using a static EnumMap encapsulates the concern of transformation within the class responsible for transformation. Its cohesive.
#Service
public class XmlTransformer {
private static final Map<demo.xml.Sense, Constraint.Sense> xmlSenseToSense;
static {
xmlSenseToSense = new EnumMap<demo.xml.Sense, Constraint.Sense> (
demo.xml.Sense.class);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.EQUALS,
Constraint.Sense.EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.GREATER_THAN_OR_EQUALS,
Constraint.Sense.GREATER_THAN_OR_EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.LESS_THAN_OR_EQUALS,
Constraint.Sense.LESS_THAN_OR_EQUALS);
}
...
}
Guava's Enums.getIfPresent() on Enum.name()
Our case was a particular specialization of this one. We do have two Enum: one we use in the application and another one we use in the core library. The core library is used by a handful of applications, by different teams. Each application views a subset of the whole functionality. The whole functionality is configured with the enums in order to switch on and off, throttle up or down, select strategies, etc.
So we ended up with:
one enum for the library, containing all the possible configurations, visible from the applications and also some library-specific
one enum for each application, containing the literals corresponding to what the application can see/touch in the library, and some application-specific
Then as we pass data down to the library, we adapt all data and also those configurations. We own all enums, so we can choose to call the same configuration with the same literal in different enums.
Enum LibraryConfig {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
FUNCTION_FOUR;
}
Enum Aplication1Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
APPL1_FUNCTION_ONE,
APPL1_FUNCTION_TWO;
}
Enum Aplication2Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_FOUR;
APPL2_FUNCTION_ONE;
}
When we need to convert from one type to another (app --> lib or lib --> app) we use the getIfPresent() method from com.google.common.base.Enums in this way:
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();
We check configLib for null value to see if there was successful conversion. This last step we use because of the APPX_FUNCTION_YYY, which are application-specific, and for the conversion on the direction lib --> app, not to pass configuration values library-specific (FUNCTION_FOUR in the example).
maven's dependency management:
Just in case anyone needs it:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
Home grown version:
You can make your own conversion using the Enum methods, but you have to take care of the exception to detect when the conversion did not succeed:
try {
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = LibraryConfig.valueOf(config1App1.name());
} catch (IllegalArgumentException iae) {
// if the conversion did not succeed
}
Ted's answer is very Javaly, but the expression
passed == PASSED ? DONE : ERROR
would do the job, too.
To me that sounds more like a conceptual problem than a programming problem. Why don't you just remove the "Simple" enum type and use the other one instead in all places in the program?
Just to make that more clear with another example: Would you really try to define an enum type for the work days in a week (Monday to Friday) and another enum for all days of a week (Monday to Sunday)?
Here is the simple enum mapper with test:
-- IMPLEMENTATION
-- ENUMS
public enum FirstEnum {
A(0), B(1);
private final int value;
private FirstEnum(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public enum SecondEnum {
C(0), D(1);
private final int valueId;
private SecondEnum(int valueId) {
this.valueId = valueId;
}
public int getValueId() {
return valueId;
}
}
--MAPPER
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.Validate;
import com.google.common.collect.Sets;
public class EnumPropertyMapping {
private final Map<?, ?> firstMap;
private final Map<?, ?> secondMap;
private final Class<?> firstType;
private final Class<?> secondType;
private EnumPropertyMapping(
Map<?, ?> firstMap, Map<?, ?> secondMap, Class<?> firstType, Class<?> secondType) {
this.firstMap = firstMap;
this.secondMap = secondMap;
this.firstType = firstType;
this.secondType = secondType;
}
public static Builder builder() {
return new Builder();
}
#SuppressWarnings("unchecked")
public <R> R getCorrespondingEnum(Object mappedEnum) {
Validate.notNull(mappedEnum, "Enum must not be NULL");
Validate.isInstanceOf(Enum.class, mappedEnum, "Parameter must be an Enum");
if (firstType.equals(mappedEnum.getClass())) {
return (R) firstMap.get(mappedEnum);
}
if (secondType.equals(mappedEnum.getClass())) {
return (R) secondMap.get(mappedEnum);
}
throw new IllegalArgumentException("Didn't found mapping for enum value: " + mappedEnum);
}
public static class Builder {
private final Map<Object, Object> firstEnumMap = new HashMap<>();
private final Map<Object, Object> secondEnumMap = new HashMap<>();
private Class<?> firstEnumType;
private Class<?> secondEnumType;
public <T extends Enum<T>> Builder addFirst(Class<T> enumType, String propertyName) {
firstEnumType = enumType;
initMap(firstEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
public <T extends Enum<T>> Builder addSecond(Class<T> enumType, String propertyName) {
secondEnumType = enumType;
initMap(secondEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
private void initMap(Map<Object, Object> enumMap, Object[] enumConstants, String propertyName) {
try {
for (Object constant : enumConstants) {
enumMap.put(PropertyUtils.getProperty(constant, propertyName), constant);
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
public EnumPropertyMapping mapEnums() {
Validate.isTrue(firstEnumMap.size() == secondEnumMap.size());
Validate.isTrue(Sets.difference(firstEnumMap.keySet(), secondEnumMap.keySet()).isEmpty());
Map<Object, Object> mapA = new HashMap<>();
Map<Object, Object> mapB = new HashMap<>();
for (Map.Entry<Object, Object> obj : firstEnumMap.entrySet()) {
Object secondMapVal = secondEnumMap.get(obj.getKey());
mapA.put(obj.getValue(), secondMapVal);
mapB.put(secondMapVal, obj.getValue());
}
return new EnumPropertyMapping(mapA, mapB, firstEnumType, secondEnumType);
}
}
}
-- TEST
import org.junit.Test;
import com.bondarenko.common.utils.lang.enums.FirstEnum;
import com.bondarenko.common.utils.lang.enums.SecondEnum;
import static junit.framework.TestCase.assertEquals;
public class EnumPropertyMappingTest {
#Test
public void testGetMappedEnum() {
EnumPropertyMapping mapping = EnumPropertyMapping.builder()
.addSecond(SecondEnum.class, "valueId")
.addFirst(FirstEnum.class, "value")
.mapEnums();
assertEquals(SecondEnum.D, mapping.getCorrespondingEnum(FirstEnum.B));
assertEquals(FirstEnum.A, mapping.getCorrespondingEnum(SecondEnum.C));
}
}

Categories

Resources