How to add a parameterized super interface in JavaPoet? - java

I am writing an annotation processor that generates JSON serialization code. Here's my annotation that I use to identify the POJOs that need a serializer
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface JsonSerialize {
}
And here's the base interface of my serializer
public interface JsonSerializer<T> {
String serialize(T t);
}
Here's the annotation processor code that looks for that annotation and generates the serializer code
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(JsonSerialize.class)) {
if (element.getKind() == ElementKind.CLASS) {
MethodSpec serializeMethod = MethodSpec
.methodBuilder("serialize")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
.returns(String.class)
.addStatement("return \"dummy string\"")
.build();
TypeSpec serializer = TypeSpec
.classBuilder(element.getSimpleName().toString() + "JsonSerializer")
.addSuperinterface(JsonSerializer.class) // THIS LINE IS WRONG
.addModifiers(Modifier.PUBLIC)
.addMethod(serializeMethod)
.build();
try {
JavaFile.builder(processingEnv.getElementUtils().getPackageOf(element).toString(), serializer)
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
But I get a compile error, because my generated class is not specifying the generic parameter in it's inheritance. How can I specify that?

Instead of passing a java.lang.Class to the addSuperinterface method, you'll need to pass something with the specific type details you have in mind. This method has two overloads - one which takes java.lang.reflect.Type (and Class is a subtype of this), and another which one which takes com.squareup.javapoet.TypeName). Technically either works, though since you are already using JavaPoet, I'd encourage trying to create the TypeName instance.
TypeName has a number of subclasses, ClassName, ParameterizedTypeName are probably the main ones to focus on here. In an annotation processor, they have some big advantages over using a Class instance - mostly that you don't need to actually be able to load or reference the class you are talking about - kind of like how you are using element.getSimpleName().toString() elsewhere in your code.
These classes have static methods to create them, which can be based on a variety of things. The one we're interested in here is this:
/** Returns a parameterized type, applying {#code typeArguments} to {#code rawType}. */
public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments)
In you code, you would use it roughly like this:
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
ClassName.get(whateverTShouldBe) //the value for T
))
...
Chance are excellent that T could eventually be generic here too, like List<String>, so you should take care to properly build the type which is passed in there - it might itself be a ParameterizedTypeName. Keep an eye on the various methods in TypeName for this too - the TypeName.get(TypeMirror) overload for example will take an already-parameterized declared type mirror and give you the expected ParameterizedTypeName back again.
With that said, according to your other code, T today cannot be generic - you look for the #JsonSerialize annotation on an Element, which means it would be the equivelent of List<T> rather than the usage of it, List<String>. Then, in this line, you make the Element into a TypeMirror to build the type name as I've described above:
.addParameter(ParameterSpec.builder(TypeName.get(element.asType()), "obj", Modifier.FINAL).build())
This means the final code would probably be
...
.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(JsonSerializer.class),//rawType
TypeName.get(element.asType()) //the value for T
))
...

Related

Trying to create a generic Kafka consumer factory

I'm trying to implement a generic Kafka consumer factory. By generic I mean that the factory will receive generics K,V that will indicate the types of the key and the value of the consumed message.
public class KafkaConsumerFactory<K, V> {
private Properties properties;
In my constructor I'm calling a method that responsible for validating that the generics K,V are compatible with the deseralization classes :
public KafkaConsumerFactory(Properties p) {
....
....
validateGenericsWithDeserializationProperties();
}
The relevant function :
private void validateGenericsWithDeserializationProperties() throws Exception {
try {
String keyDeserializerClass = (String) (this.properties.get(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG));
String valueDeserializerClass = (String) (this.properties.get(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG));
Deserializer<K> keyDeserializer = (Deserializer<K>) Class.forName(keyDeserializerClass).getConstructor().newInstance();
Deserializer<V> valueDeserializer = (Deserializer<V>) Class.forName(valueDeserializerClass ).getConstructor().newInstance();
System.out.println(keyDeserializer.toString());
System.out.println(valueDeserializer.toString());
} catch (Exception e) {
throw new Exception("There is an incompatibility with the deserialization parameters in the properties with the generics <K,V>");
}
}
But it seems that when I pass generics K,V that aren't compatible with the deserializer class the code doesn't throw exception , for example:
K=String,
V=Long,
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
I was expecting an exception on the downcasting on the following lines :
Deserializer<K> keyDeserializer = (Deserializer<K>)(Class.forName(keyDeserializerClass).getConstructor().newInstance());
Deserializer<V> valueDeserializer = (Deserializer<V>)Class.forName(valueDeserializerClass).getConstructor().newInstance();
I understand that those lines won't throw exception because of erasure of generic types.
Is there any other way to check for incompatibility between the deserialization class to the type of generic before trying to consume data?
Deserializer<K> keyDeserializer = (Deserializer<K>) Class.forName(keyDeserializerClass).getConstructor().newInstance();
Deserializer<V> valueDeserializer = (Deserializer<V>) Class.forName(valueDeserializerClass ).getConstructor().newInstance();
Why do you want to explicity create Deserializer object?
It will automatically be created as per the one specified in the properties.
If you want to verify (make sure) the appropriate deserializer is put for the appropriate key/value type, you can take the keyClass, valueClass in the constructor of your KafkaConsumerFactory
public KafkaConsumerFactory(Properties p, Class<K> keyClass, Class<V> valueClass) {
p.putIfAbsent("key.deserializer", getDeserializer(keyClass).getName());
p.putIfAbsent("value.deserializer", getDeserializer(valueClass).getName());
}
and then you can automatically map the keyClass, valueClass to appropriate deserializer and override them in the properties.
Class<Deserializer> getDeserializer(Class clazz) {
if(Long.class.equals(clazz)) { return LongDeserializer.class; }
else if (...)
...
}
If you want to support new custom deserializers, you can use an interface like CustomDeserializer for example that has a method called canBeProcessed(Class clazz) which determines if the object can be deserialized and ensure that this interface is implemented by all your custom deserializers and that the method is overriden.
If you are looking to support already existing deserializers, you can write the code to check the compatability of the deserializer with a certain class in your KafkaConsumerFactory itself.
Usually, serializers/deserializers are mapped to certain class(es), which means that you don't need an object to verify if it can be serialized/deserialized because the data in the object has pretty less (if not nothing) to do with the actual serialization/deserialization.
In case, if the class type information isn't enough for finding out whether it's object can be deserialized or not, the option left is to create a dummy object and then pass it to the deserialize() method. If it raises an exception, then it is invalid, not otherwise.

How to properly override JacksonAnnotationIntrospector._findAnnotation to replace an annotation of the element

I am trying to create some classes serializable by Jackson. I want to annotate some elements with standard Jackson annotation (let's consider JsonIgnore for this example) but I want them to have effect only in my specific mapper.
So I decided to create my own annotations like standard ones (e. g. MyJsonIgnore) and process them only in an annotation introspector used by my mapper. I've found overridable method _findAnnotation. Javadoc says the following:
...overridable that sub-classes may, if they choose to,
mangle actual access block access ("hide" annotations)
or perhaps change it.
I've found a way to block some annotations (however it involves overriding _hasAnnotation, not _findAnnotation) but I am completely stuck with changing annotations.
Here is some minimal example of what I am trying to do:
object Mappers {
/**
* Same as JsonIgnore but mapper-specific
*/
annotation class MyJsonIgnore(val value: Boolean = true)
private class MyIntrospector : JacksonAnnotationIntrospector() {
override fun <A : Annotation> _findAnnotation(
annotated: Annotated,
annoClass: Class<A>
): A {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
val mji = _findAnnotation(annotated, MyJsonIgnore::class.java)
return JsonIgnore(mji.value) // Does not compile, type mismatch
// return JsonIgnore(mji.value) as A // Does not compile, annotation class cannot be instantiated, same as Java, see below
}
return super._findAnnotation(annotated, annoClass)
}
}
fun myMapper(): ObjectMapper {
return ObjectMapper().setAnnotationIntrospector(MyIntrospector())
}
}
I also cannot do it with Java:
public class Mappers {
/**
* Same as JsonIgnore but mapper-specific
*/
public #interface MyJsonIgnore {
boolean value() default true;
}
private static class MyIntrospector extends JacksonAnnotationIntrospector {
#Override
protected <A extends Annotation> A _findAnnotation(Annotated annotated,
Class<A> annoClass) {
if (annoClass == JsonIgnore.class && _hasAnnotation(annotated, MyJsonIgnore.class)) {
MyJsonIgnore mji = _findAnnotation(annotated, MyJsonIgnore.class);
return new JsonIgnore(mji.value()); // Does not compile, JsonIgnore is abstract
}
return super._findAnnotation(annotated, annoClass);
}
}
static ObjectMapper myMapper() {
return new ObjectMapper().setAnnotationIntrospector(new MyIntrospector())
}
}
So what is the supposed way to change annotations by overriding this method? Is there any? Is my approach right or should I do it other way?
The main problem here is that you can't instantiate the annotation class. There is one solution though: you could store one annotation as a member of another annotation like this:
#Retention(AnnotationRetention.RUNTIME) // don't forget
#Target(AnnotationTarget.FIELD) // these annotations
annotation class MyJsonIgnore(val value: Boolean = true, val jsonIgnore: JsonIgnore = JsonIgnore())
So MyJsonIgnore will have an instantiated JsonIgnore inside. And then you can use it in your AnnotationIntrospector:
private class MyIntrospector : JacksonAnnotationIntrospector() {
override fun <A : Annotation> _findAnnotation(
annotated: Annotated,
annoClass: Class<A>
): A? {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
val mji = _findAnnotation(annotated, MyJsonIgnore::class.java)
if (mji?.value == true) {
return mji.jsonIgnore as A // this cast should be safe because we have checked
// the annotation class
}
}
return super._findAnnotation(annotated, annoClass)
}
}
I've tested this with the following class
class Test {
#MyJsonIgnore
val ignoreMe = "IGNORE"
val field = "NOT IGNORE"
}
and method
fun main() {
println(Mappers.myMapper().writeValueAsString(Test()))
println(ObjectMapper().writeValueAsString(Test()))
}
and the output was
{"field":"NOT IGNORE"}
{"ignoreMe":"IGNORE","field":"NOT IGNORE"}
So here are my further thoughts. Kirill Simonov's answer is right and typesafe (an alternative would be to create an annotation instance using Kotlin reflection but it is not typesafe).
Here are some problems with my original code and thoughts about the original approach:
You should consistently override _hasAnnotation and _getAnnotation
You cannot be sure that _getAnnotation will be called after _hasAnnotation check. You cannot be sure which of them will be used to check your replaced annotation (#JsonIgnore in my case) without looking into JacksonAnnotationIntrospector code. It seems that overriding them consistently would be a good practice. So we should also add the following override to our class if we want to use this approach:
override fun <A : Annotation> _hasAnnotation(
annotated: Annotated,
annoClass: Class<A>
): Boolean {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
return true
}
return super._hasAnnotation(annotated, annoClass)
}
_getAnnotation return type should be nullable
This was correctly fixed by Kirill but was not explicitly pointed out. _getAnnotation can and will return null sometimes.
You (probably) cannot have one magic MyConditional annotation.
Kirill's answer may encourage you to create something like a conditional annotation for all the jackson annotation which could be used as follows:
#MyConditional([
JsonIgnore, JsonProperty("propertyName")
])
You just cannot have polymorphic annotation parameter. You would have to create My* for every Jackson annotation you need and for annotation with parameters it's not as neat as with #MyJsonIgnore.
You can try to make a repeatable annotation which would be applied like below and instantiated using reflection.
#MyConditional(
clazz = JsonProperty::class.java,
args = [
// Ah, you probably cannot have an array of any possible args here, forget it.
]
)
_hasAnnotation and _getAnnotation are not the only ways which JacksonAnnotationIntrospector uses to get or check for annotations
After using similar approach to create conditional #JsonProperty annotation I've noticed that it does not work for enum elements. After some debugging I found out that findEnumValues method uses java.lang.reflect.Field::getAnnotation directly (due to "various reasons" mentioned in deprecated findEnumValue). If you want your conditional annotation to work you should override (at least) findEnumValues.
Be careful with ObjectMapper::setAnnotationIntrospector
Well, its Javadoc explicitly says it: be careful. It replaces the whole annotation introspector of your mapper removing all the added (chained) by modules Introspectors. It did not appear in my code in the question (it was for the sake of creating minimal example) but actually I've accidentally broke deserialization with KotlinModule. You may want to consider implementing JacksonModule and appending your introspector to existing ones.
Consider another approach: implementing functionality-specific methods in NopAnnotationIntrospector.
At the end I ended up with this approach (mostly because of 4.). I needed to override findEnumValues and hasIgnoreMarker and it was enough for me. It involved a bit of copy-paste code from JacksonAnnotationMapper but unless you have to make a lot of the annotation conditional it might work (in any case implementing it involves a lot of boilerplate code). This way it's likely that you really want to chain this introspector, not to set it.

Get field class in annotations processor

I am writing my first Annotations processor and having trouble with something that seems trivial but I cannot find any information about it.
I have a element annotated with my annotation
#MyAnnotation String property;
When I get this property as a element in my processor I can not seem to get the type of the element in any way. In this case a would want to get a Class or TypeElement instance representing String.
I tried instantiating a class object of the container type with Class.forName() but it threw a ClassNotFoundException. I think this is because I do not have access to the class loader containing the class?
When running your annotation processor, you don't have access to the compiled classes. The point of annotation processing is that it happens pre-compile.
Instead, you need to create an annotation processor that specifically handles your annotation type, then use the mirror API to access the field. For example:
#SupportedAnnotationTypes("com.example.MyAnnotation")
public class CompileTimeAnnotationProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// Only one annotation, so just use annotations.iterator().next();
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(
annotations.iterator().next());
Set<VariableElement> fields = ElementFilter.fieldsIn(elements);
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
// Validate fullTypeClassName
}
return true;
}
}
For the validation, you cannot use any classes which have yet to be compiled (including those that are about to be compiled with the annotation) using something like MyType.class. For these, you must use strings only. That is because annotation processing occurs during a pre-compiling phase known as "source generation", which is what allows you to generate source code before the compiler runs using annotations.
An example validation verifying that the field type is java.lang.String (which is already compiled):
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
if (!String.class.getName().equals(fullTypeClassName)) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field type must be java.lang.String", field);
}
}
Resources
Main APT Page
Mirror API Javadocs (Java 7 and older)
Edit: Mirror API Javadocs (Java 8)
Note that the mirror API is now standardized in Java 8 under javax.lang.model and the old API is deprecated. See this blog post for more information. If you've been using the javax classes, then you don't need to worry.
Edit:
I want to get the field type to get annotations on that type. But this does not seem like it will be possible?
Indeed it is possible! This can be done using more methods on the TypeMirror:
if (fieldType.getKind() != TypeKind.DECLARED) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field cannot be a generic type.", field);
}
DeclaredType declaredFieldType = (DeclaredType) fieldType;
TypeElement fieldTypeElement = (TypeElement) declaredFieldType.asElement();
From here, you have two choices:
If the annotation you're trying to find is already compiled (i.e. it's from another library) then you can reference the class directly to get the annotation.
If the annotation you're trying to find is not compiled (i.e. it's being compiled in the current call to javac that's running the APT) then you can reference it via AnnotationMirror instances.
Already Compiled
DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation(
DifferentAnnotation.class);
// Process diffAnn
Very straight-forward, this gives you direct access to the annotation itself.
Not Compiled
Note that this solution will work regardless of whether or not the annotation is compiled, it's just not as clean as the code above.
Here are a couple methods I wrote once to extract a certain value from an annotation mirror by its class name:
private static <T> T findAnnotationValue(Element element, String annotationClass,
String valueName, Class<T> expectedType) {
T ret = null;
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
DeclaredType annotationType = annotationMirror.getAnnotationType();
TypeElement annotationElement = (TypeElement) annotationType
.asElement();
if (annotationElement.getQualifiedName().contentEquals(
annotationClass)) {
ret = extractValue(annotationMirror, valueName, expectedType);
break;
}
}
return ret;
}
private static <T> T extractValue(AnnotationMirror annotationMirror,
String valueName, Class<T> expectedType) {
Map<ExecutableElement, AnnotationValue> elementValues = new HashMap<ExecutableElement, AnnotationValue>(
annotationMirror.getElementValues());
for (Entry<ExecutableElement, AnnotationValue> entry : elementValues
.entrySet()) {
if (entry.getKey().getSimpleName().contentEquals(valueName)) {
Object value = entry.getValue().getValue();
return expectedType.cast(value);
}
}
return null;
}
Let's say that you're looking for the DifferentAnnotation annotation and your source code looks like this:
#DifferentAnnotation(name = "My Class")
public class MyClass {
#MyAnnotation
private String field;
// ...
}
This code will print My Class:
String diffAnnotationName = findAnnotationValue(fieldTypeElement,
"com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);

Java - Alternatives to forcing subclass to have a static method

I often find I want to do something like this:
class Foo{
public static abstract String getParam();
}
To force a subclasses of Foo to return a parameter.
I know you can't do it and I know why you can't do it but the common alternative of:
class Foo{
public abstract String getParam();
}
Is unsatisfactory because it requires you to have an instance which is not helpful if you just want to know the value of the parameter and instantiating the class is expensive.
I'd be very interested to know of how people get around this without getting into using the "Constant Interface" anti pattern.
EDIT: I'll add some more detail about my specific problem, but this is just the current time when I've wanted to do something like this there are several others from the past.
My subclasses are all data processors and the superclass defines the common code between them which allows them to get the data, parse it and put it where it needs to go.
The processors each require certain parameters which are held in an SQL database. Each processor should be able to provide a list of parameters that it requires and the default values so the configuration database can be validated or initialised to defaults by checking the required parameters for each processor type.
Having it performed in the constructor of the processor is not acceptable because it only needs to be done once per class not once per object instance and should be done at system startup when an instance of each type of class may not yet be needed.
The best you can do here in a static context is something like one of the following:
a. Have a method you specifically look for, but is not part of any contract (and therefore you can't enforce anyone to implement) and look for that at runtime:
public static String getParam() { ... };
try {
Method m = clazz.getDeclaredMethod("getParam");
String param = (String) m.invoke(null);
}
catch (NoSuchMethodException e) {
// handle this error
}
b. Use an annotation, which suffers from the same issue in that you can't force people to put it on their classes.
#Target({TYPE})
#Retention(RUNTIME)
public #interface Param {
String value() default "";
}
#Param("foo")
public class MyClass { ... }
public static String getParam(Class<?> clazz) {
if (clazz.isAnnotationPresent(Param.class)) {
return clazz.getAnnotation(Param.class).value();
}
else {
// what to do if there is no annotation
}
}
I agree - I feel that this is a limitation of Java. Sure, they have made their case about the advantages of not allowing inherited static methods, so I get it, but the fact is I have run into cases where this would be useful. Consider this case:
I have a parent Condition class, and for each of its sub-classes, I want a getName() method that states the class' name. The name of the sub-class will not be the Java's class name, but will be some lower-case text string used for JSON purposes on a web front end. The getName() method will not change per instance, so it is safe to make it static. However, some of the sub-classes of the Condition class will not be allowed to have no-argument constructors - some of them I will need to require that some parameters are defined at instantiation.
I use the Reflections library to get all classes in a package at runtime. Now, I want a list of all the names of each Condition class that is in this package, so I can return it to a web front end for JavaScript parsing. I would go through the effort of just instantiating each class, but as I said, they do not all have no-argument constructors. I have designed the constructors of the sub-classes to throw an IllegalArgumentException if some of the parameters are not correctly defined, so I cannot merely pass in null arguments. This is why I want the getName() method to be static, but required for all sub-classes.
My current workaround is to do the following: In the Condition class (which is abstract), I have defined a method:
public String getName () {
throw new IllegalArugmentException ("Child class did not declare an overridden getName() method using a static getConditionName() method. This must be done in order for the class to be registerred with Condition.getAllConditions()");
}
So in each sub-class, I simply define:
#Override
public String getName () {
return getConditionName ();
}
And then I define a static getConditionName() method for each. This is not quite "forcing" each sub-class to do so, but I do it in a way where if getName() is ever inadvertently called, the programmer is instructed how to fix the problem.
It seems to me you want to solve the wrong problem with the wrong tool. If all subclasses define (can't really say inherit) your static method, you will still be unable to call it painlessly (To call the static method on a class not known at compile time would be via reflection or byte code manipulation).
And if the idea is to have a set of behaviors, why not just use instances that all implement the same interface? An instance with no specific state is cheap in terms of memory and construction time, and if there is no state you can always share one instance (flyweight pattern) for all callers.
If you just need to couple metadata with classes, you can build/use any metadata facility you like, the most basic (by hand) implementation is to use a Map where the class object is the key. If that suits your problem depends on your problem, which you don't really describe in detail.
EDIT: (Structural) Metadata would associate data with classes (thats only one flavor, but probably the more common one). Annotations can be used as very simple metadata facility (annotate the class with a parameter). There are countless other ways (and goals to achieve) to do it, on the complex side are frameworks that provide basically every bit of information designed into an UML model for access at runtime.
But what you describe (processors and parameters in database) is what I christened "set of behaviors". And the argument "parameters need to be loaded once per class" is moot, it completely ignores the idioms that can be used to solve this without needing anything 'static'. Namely, the flyweight pattern (for having only once instance) and lazy initialization (for doing work only once). Combine with factory as needed.
I'm having the same problem over and over again and it's hard for me to understand why Java 8 preferred to implement lambda instead of that.
Anyway, if your subclasses only implement retrieving a few parameters and doing rather simple tasks, you can use enumerations as they are very powerful in Java: you can basically consider it a fixed set of instances of an interface. They can have members, methods, etc. They just can't be instanciated (as they are "pre-instanciated").
public enum Processor {
PROC_IMAGE {
#Override
public String getParam() {
return "image";
}
},
PROC_TEXT {
#Override
public String getParam() {
return "text";
}
}
;
public abstract String getParam();
public boolean doProcessing() {
System.out.println(getParam());
}
}
The nice thing is that you can get all "instances" by calling Processor.values():
for (Processor p : Processorvalues()) {
System.out.println(String.format("Param %s: %s", p.name(), p.getParam()));
p.doProcessing();
}
If the processing is more complex, you can do it in other classes that are instanciated in the enum methods:
#Override
public String getParam() {
return new LookForParam("text").getParam();
}
You can then enrich the enumeration with any new processor you can think of.
The down side is that you can't use it if other people want to create new processors, as it means modifying the source file.
You can use the factory pattern to allow the system to create 'data' instances first, and create 'functional' instances later. The 'data' instances will contain the 'mandatory' getters that you wanted to have static. The 'functional' instances do complex parameter validation and/or expensive construction. Of course the parameter setter in the factory can also so preliminary validation.
public abstract class Processor { /*...*/ }
public interface ProcessorFactory {
String getName(); // The mandatory getter in this example
void setParameter(String parameter, String value);
/** #throws IllegalStateException when parameter validation fails */
Processor construct();
}
public class ProcessorA implements ProcessorFactory {
#Override
public String getName() { return "processor-a"; }
#Override
public void setParameter(String parameter, String value) {
Objects.requireNonNull(parameter, "parameter");
Objects.requireNonNull(value, "value");
switch (parameter) {
case "source": setSource(value); break;
/*...*/
default: throw new IllegalArgumentException("Unknown parameter: " + parameter);
}
}
private void setSource(String value) { /*...*/ }
#Override
public Processor construct() {
return new ProcessorAImpl();
}
// Doesn't have to be an inner class. It's up to you.
private class ProcessorAImpl extends Processor { /*...*/ }
}

Java - Factory Method that returns generic Base type

I'm trying to genericize a factory method that returns
a generic Base class. It works, but I'm getting the
"BaseClass is a raw type..." warning.
I've read through the Java docs on Generic methods,
but I'm still not quite getting how to accomplish this.
Here's some code:
Class #1
//base abstract class
public abstract class BaseFormatter<T>
{
public abstract String formatValue(T value);
}
Class #2
//two implementations of concrete classes
public class FooFormatter extends BaseFormatter<Integer>
{
#Override
public String formatValue(Integer value)
{
//return a formatted String
}
}
Class #3
public class BarFormatter extends BaseFormatter<String>
{
#Override
public String formatValue(String value)
{
//return a formatted String
}
}
Factory Method in a separate class
public static BaseFormatter getFormatter(Integer unrelatedInteger)
{
if (FOO_FORMATTER.equals(unrelatedInteger))
return new FooFormatter();
else if (BAR_FORMATTER.equals(unrelatedInteger))
return new BarFormatter();
//else...
}
Call to the Factory Method from elsewhere in the code
BaseFormatter<Integer> formatter = getFormatter(someInteger);
formatter.formatValue(myIntegerToFormat);
The problem is the getFormatter() method warns that BaseFormatter is
a raw type, which it is. I've tried various things like BaseFormatter
et al. I, of course, want the return type to be generic, as in the declared
BaseFormatter in the calling method.
Note that the formatter type is not based on class type. e.g. not all Integer
values are formatted with a FooFormatter. There are two or three different
ways an Integer (or String, or List) can be formatted. That's what the
param unrelatedInteger is for.
Thanks in advance for any feedback.
If getFormatter is defined in BaseFormatter, then use:
public static BaseFormatter<T> getFormatter(Integer unrelatedInteger)
If getFormatter is defined in another class than BaseFormatter, then use:
public static BaseFormatter<?> getFormatter(Integer unrelatedInteger)
You're actuaaly saying that there's no connection between the typed parameter of BaseFormatter and the unrelatedInteger that is passed as argument to the getFormatter method.
I get some other warning:
Uncehcked Assignment: BaseFormatter to BaseFormatter<Integer>
This warning is worse than the one you indicated. It warns that this user code might try to insert a BaseFormatter<String> into BaseFormatter<Integer>, something that will be noticed only when fails in runtime... Consider a user accidentally uses you factory method like such:
BaseFormatter<Integer> myUnsafeFormatter =
FormatterFactory.getFormatter(unrelatedIntegerForBarFormatter);
The compiler cannot relate the unrelatedInteger with the parameterized type of the returned BaseFormatter.
Alternitavely, I'd let the user explicitly use the concrete formatter constructors. Any common code shared by all formatters could be put into FormatterUtils class (just don't let that utils class to grow to much...).
Some type systems in academic languages can express a so-called dependent sum. Java certainly cannot; so what, sensibly, could be the type of the object returned by the getFormatter method? The best we can do is BaseFormatter< ? extends Object >, or BaseFormatter< ? > for short, as Integer and String have only Object in common.
I think the original post begs the question, why must we use an integer to decide what formatter to return, and if the type of formatter would not be known by the caller, why would the caller need a stronger variable type than BaseFormatter< ? >?

Categories