Generic mapping of Value Objects with mapstruct - java

Trying to use value objects in my business model, I'm facing issues with the following code :
#Mapper
public abstract class TestMapstruct {
public ValueObjectA mapA(String value){
return new ValueObjectA(value);
}
public abstract BusinessObject map(DTO dto);
#Value
public static class ValueObjectA {
private String a;
}
#Value
public static class ValueObjectB {
private String b;
}
#Data
public static class BusinessObject {
private ValueObjectA a;
private ValueObjectB b;
}
#Data
public static class DTO {
private String a;
private String b;
}
}
Missing mapping (String -> ValueObjectB) would lead to the following compilation error message :
Can't map property "java.lang.String b" to "test.ValueObjectB b". Consider to declare/implement a mapping method: "test.ValueObjectB map(java.lang.String value)".
I totally understand this, but I would rather not to declare a method for each of my ValueObjects (could have dozens in a project).
Is there a generic way to declare (String -> ValueObject) mapping method ?

There is no generic way to do this if you don't have a common interface between all the methods.
However, if you have a common interface then it is possible. For example:
public interface ValueObject<T> {
T getValue();
void setValue(T value);
}
Then you need a helper mapper:
public interface ValueObjectMapper {
static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
return valueObject == null ? null : valueObject.getValue();
}
static <V extends ValueObject<T>, T> V mapFromValueObject(T value, #TargetType Class<V> valueObjectClass) {
if (value == null) {
return null;
}
try {
V valueObject = valueObjectClass.getDeclaredConstructor().newInstance();
valueObject.setValue(value);
return valueObject;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Edit: Add example with immutable Value objects.
In case you want your value objects to be immutable then you can do the following:
public interface ValueObject<T> {
T getValue();
}
public interface ValueObjectMapper {
static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
return valueObject == null ? null : valueObject.getValue();
}
static <V extends ValueObject<T>, T> V mapFromValueObject(T value, #TargetType Class<V> valueObjectClass) {
if (value == null) {
return null;
}
try {
V valueObject = valueObjectClass.getDeclaredConstructor(value.getClass()).newInstance(value);
return valueObject;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Note: To make this work you will have to make sure that all your ValueObjects have a constructor with that value.

Related

JOOQ choose field Converter by another field

I want to convert between Set of Enum POJO and String[] Database(postgres) column.
and the enum class would be changed by another field type.
So I can say Enum class which's using in fooSet is changable and it's up to field type.
I know it's a messy. but I need a help.
Below are models
public interface A {
enum B implements A {
step1,
step2,
step3
}
enum C implements A {
step4,
step5,
step6
}
}
public abstract class Foo {
private String type;
}
public abstract class FooA {
private Set<B> fooSet;
}
public abstract class FooB {
private Set<C> fooSet;
}
I want to make a Converter like below.
SetOfEnumConverter<U extends Enum<U> & A> implements Converter<String[], Set<U>> {
#Override
public Set<U> from(String[] databaseObject) {
if (databaseObject == null) {
return null;
}
return Arrays.stream(databaseObject)
.map(x -> U.valueOf(U.class, x)). // here's the problem point
.collect(Collectors.toSet());
}
#Override
public String[] to(Set<U> userObject) {
if (userObject == null || userObject.isEmpty()) {
return null;
}
String[] strings = userObject.stream()
.map(Enum::name)
.toArray(String[]::new);
return ArrayUtils.isEmpty(strings) ? new String[0]: strings;
}
#Override
public Class<String[]> fromType() {
return String[].class;
}
#Override
public Class<Set<U>> toType() {
return (Class) TreeSet.class;
}
}
But the problem is I can't point .class attribute from a generic type maybe because of Generic Type erasure.
So, What I want to do is mapping the setEnum class to be used in the field fooSet according to the field type.
Because I have to make a single table for Foo and map from FooA, FooB and FooZ.
You can't do this without passing an actual Class<U> reference to your converter, e.g. like this:
SetOfEnumConverter<U extends Enum<U> & A> implements Converter<String[], Set<U>> {
final Class<U> u;
SetOfEnumConverter(Class<U> u) {
this.u = u;
}
// ...
}
And inside of the converter, you can use:
Enum.valueOf(u, x)
To look up arbitrary enum values by their names. Then, instantiate it with e.g.
new SetOfEnumConverter<>(MyEnum.class);

Java class property with type unknown at compile time

I have a Java class with a property that can be either a String, a boolean or a List.
What would be the best approach to declare that property type? Knowing that I can only know its type at Runtime.
My first approach was to define that property with type Object, but not sure if there's a better way to achieve this.
private final String expression;
private Object expressionValue;
ParsedExpression(String expression, Person person) {
this.expression= expression;
expressionEvaluator(person);
}
private void expressionEvaluator(Person person) {
switch (this.expression) {
case "name":
expressionValue = person.getName();
break;
case "adult":
expressionValue = person.isAdult();
break;
case "addresses":
expressionValue = person.getAddresses();
break;
default:
throw new RuntimeException("Property does not exist on type Person");
}
}
If you have a very few types and not going to change them you can use a generic class with a private constructor and a few factory methods which allow creating an instance only with specified type parameters:
public final class ExpressionValue<T> {
private final T value;
private ExpressionValue(T value) {this.value = value;}
public Optional<String> getAsString() {
return (value instanceof String) ? Optional.of((String)value) : Optional.empty();
}
public Optional<Boolean> getAsBoolean() {
return (value instanceof Boolean) ? Optional.of((Boolean) value) : Optional.empty();
}
// in this context empty collection and empty optional mean different things
#SuppressWarnings("OptionalContainsCollection")
public Optional<List<?>> getAsList() {
return (value instanceof List) ? Optional.of((List<?>) value) : Optional.empty();
}
public void use(Consumer<? super String> stringUser, Consumer<? super Boolean> booleanUser, Consumer<? super List<?>> listUser) {
getAsString().ifPresent(stringUser);
getAsBoolean().ifPresent(booleanUser);
getAsList().ifPresent(listUser);
}
public static ExpressionValue<String> fromString(String string) {
return new ExpressionValue<>(string);
}
public static ExpressionValue<Boolean> fromBoolean(boolean bool) {
return new ExpressionValue<>(bool);
}
public static ExpressionValue<List<?>> fromList(List<?> list) {
return new ExpressionValue<>(list);
}
}
If you have many available types or need more flexibility of types you can use a hierarchical approach. In this case, you need to create an interface with common String, boolean and List methods, and then create implementations of this interface with all available type parameters. It may look like this:
public interface ExpressionValue<T> {
boolean check();
}
public abstract class ExpressionValueSkeleton<T> implements ExpressionValue<T> {
private T value;
public ExpressionValueSkeleton(T value) {
this.value = value;
}
protected T getValue() {
return value;
}
}
public class StringExpressionValue extends ExpressionValueSkeleton<String> {
public StringExpressionValue(String value) {
super(value);
}
#Override
public boolean check() {
return !getValue().isEmpty();
}
}
public class BooleanExpressionValue extends ExpressionValueSkeleton<Boolean> {
public BooleanExpressionValue(Boolean value) {
super(value);
}
#Override
public boolean check() {
return getValue();
}
}
public class ListExpressionValue<E> extends ExpressionValueSkeleton<List<E>> {
public ListExpressionValue(List<E> value) {
super(value);
}
#Override
public boolean check() {
return !getValue().isEmpty();
}
}
If you want perform some operations which are available only for some types you can use Acyclic Visitor pattern.

How to initializing Class<T> at interface default method?

I have an interface:
public interface ITransformer<S,T>{
public void transform(S source,T target);
default String getTransformerName(){
Class<S> s;
Class<T> t;
return s.getName() + t.getName(); //*********
}
}
the error message the starred line:
The local variable s may not have been initialized
The local variable t may not have been initialized
I would like to use this method to return a string with [S.classname][T.classname] . Please let me know how to achieve this or is this impossible to do at interface ?
Update: Jan 12
My purpose of doing this is due to the fact that this class will be in framework and I want to reduce the human error as much as possible.. I am changing the code as follows:
public interface ITransformer<S,T>{
public void transform(S source,T target);
public FieldEntry<S, T> getTransformerName();
}
public class FieldEntry<S,T> implements Comparable<FieldEntry> {
private Class<S> s;
private Class<T> t;
public FieldEntry(Class<S> s,Class<T> t){
this.s = s;
this.t = t;
}
public String getEntryName(){
return s.getName() + t.getName();
}
#Override
public int compareTo(FieldEntry entry) {
if(entry == null) throw new IllegalArgumentException("The argument to compare cannot be null!");
return entry.getEntryName().compareTo(this.getEntryName());
}
}
In order to demonstrate why this can’t work, you may change your class to
public interface ITransformer<S,T>{
public void transform(S source,T target);
static <In,Out> ITransformer<In,Out> noOp() {
return (source,target) -> {};
}
static void main(String... arg) {
ITransformer<String,Integer> t1 = noOp();
ITransformer<Long,Thread> t2 = noOp();
System.out.println(t1 == (Object)t2);
}
}
Running this will print true. In other words, both functions are represented by the same instances, so there can’t be and property allowing to recognize their different type.
Generally, when two functions (lambda expressions or method references) exhibit the same behavior, a JVM may represent them by the same implementation type or even the same instance.
Even for non-interface classes, this doesn’t work due to Type Erasure. It only works when you have a reifiable (i.e. non-generic) type extending or implementing a generic type.
It's a little bit dangerous and I wouldn't used this in production (because you should cover in your code all possible use cases of your interface), but you can use reflection for it:
public interface ITransformer<S, T> {
public void transform(S source, T target);
default String getTransformerName() {
Type[] genericInterfaces = this.getClass().getGenericInterfaces();
ParameterizedType parameterizedType = null;
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType paramInterface = (ParameterizedType) genericInterface;
if (paramInterface.getRawType().equals(ITransformer.class)) {
parameterizedType = paramInterface;
break;
}
}
}
if (parameterizedType == null) {
throw new IllegalStateException("!");
}
return parameterizedType.getActualTypeArguments()[0].getTypeName() + parameterizedType.getActualTypeArguments()[1].getTypeName();
}
}
public class StringToIntegerTransfomer implements ITransformer<String, Integer> {
#Override
public void transform(String source, Integer target) {
}
}
public interface StringToNumberTransfomer<T extends Number> extends ITransformer<String, T> {
}
public class StringToLongTransfomer implements StringToNumberTransfomer<Long>, ITransformer<String, Long> {
#Override
public void transform(String source, Long target) {
}
}
#Test
public void test() {
ITransformer<String, Integer> intTransformer = new StringToIntegerTransfomer();
ITransformer<String, Long> longTransformer = new StringToLongTransfomer();
ITransformer<String, String> stringTransformer = new ITransformer<String, String>() {
#Override
public void transform(String source, String target) {
}
};
ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>() {
#Override
public void transform(String source, Double target) {
}
};
System.out.println(String.format("intTransformer: %s", intTransformer.getTransformerName()));
System.out.println(String.format("longTransformer: %s", longTransformer.getTransformerName()));
System.out.println(String.format("stringTransformer: %s", stringTransformer.getTransformerName()));
System.out.println(String.format("doubleTransformer: %s", doubleTransformer.getTransformerName()));
}
Output for this snippet:
intTransformer: java.lang.Stringjava.lang.Integer
longTransformer: java.lang.Stringjava.lang.Long
stringTransformer: java.lang.Stringjava.lang.String
java.lang.IllegalStateException: !
This code has one restriction, you should say implements ITransformer<S, T> for all implementations of ITransformer. That why I have got IllegalStateException for this line ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>(). But you can improve this code.
Better option is to use some base implementation of interface and pass source and target classes into constructor:
public interface ITransformer<S, T> {
void transform(S source, T target);
String getTransformerName();
}
public abstract class BaseITransformer<S, T> implements ITransformer<S, T> {
private final Class<S> sourceClass;
private final Class<T> targetClass;
public BaseITransformer(Class<S> sourceClass, Class<T> targetClass) {
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
public String getTransformerName() {
return sourceClass.getName() + targetClass.getName();
}
}
In Java it is impossible to get a Class<S>, unless you already know which class S is, or something else that knows which class S is gives you one.

How to create generic enum converter?

I want to write a generic class that can be used like new EnumConverter(MyEnum.class);
But hwo to I have to write that class so that it works with generic calls to enum.values()?
public class EnumConverter {
public EnumConverter(Class<Enum<?>> type) {
this.type = type;
}
public EnumConverter convert(String text) {
//error: Type mismatch: cannot convert from element type capture#1-of ? to Enum<?>
for (Enum<?> candidate : type.getDeclaringClass().getEnumConstants()) {
if (candidate.name().equalsIgnoreCase(text)) {
return candidate;
}
}
}
}
Your return type of method convert is wrong: Shoud be Enum instead of EnumConverter. And then you don't have to call getDeclaringClass() but use the Class you already have in type.
I would suggest an even more generic approach:
public static class EnumConverter<T extends Enum<T>>
{
Class<T> type;
public EnumConverter(Class<T> type)
{
this.type = type;
}
public Enum<T> convert(String text)
{
for (Enum<T> candidate : type.getEnumConstants()) {
if (candidate.name().equalsIgnoreCase(text)) {
return candidate;
}
}
return null;
}
}
your question is inconsistent and not valid code:
1) this.type is not defined as a field
2) you define convert to return EnumConverter but return an Enum
to return a enum value of an enum from text you do not need generic stuff. you simply use:
Enum.valueOf(MyEnum.class, "TWO")
Putting together the two best answers, you get a clean one liner:
public <T extends Enum<T>> T getEnumValue(Class<T> type, String str) {
return Enum.valueOf(type, str);
}
Following is not a class like requested, but still an improvement over some other answers because it produces no warnings (java 11/IDEA 2020)
static <Dest extends Enum<Dest>> Dest
convertEnumStrict(final Class<Dest> destinationClassType, final Enum<?> sourceEnum) {
if (sourceEnum == null) {
return null;
}
return Dest.valueOf(destinationClassType, sourceEnum.name());
}
static <Dest extends Enum<Dest>> Dest
convertEnumOrNull(final Class<Dest> destinationClassType, final Enum<?> sourceEnum) {
try {
return convertEnumStrict(destinationClassType, sourceEnum);
} catch (IllegalArgumentException e) {
return null;
}
}
In case if some looking for alternative solution with out for loop.
// classType is target Enum
public static Enum<?> toEnum(final Class<? extends Enum> classType, final Enum<?> enumObj) {
if (enumObj == null) {
return null;
} else {
return enumObj.valueOf(classType, enumObj + "");
}
Usage:
TestEnum1 enum1 = (TestEnum1) toEnum(TestEnum1.class, TestEnum1.HELLO_ENUM);

Can generics allow the Java compiler to check the type of keys and values in a map?

I am in a situation where I want to have a map where the keys are an interface class, and the corresponding value is a class which implements that interface. In other words the key and value type is related.
My current implementation of the method which adds to the map, and gets an instance of the implementation class looks like:
// should be something like Class<T>, Class<? extends T>
static Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>> ();
public static <T> void add(Class<T> interfaceT,
Class<? extends T> implementationT) {
map.put(interfaceT, implementationT);
}
public static <T> T get(Class<T> interfaceT) {
// cast caused by definition not complete.
Class<T> implementationT = (Class<T>) map.get(interfaceT);
// try catch stuff omitted
T t = implementationT.newInstance();
return t;
}
My question is:
Can I define the "map" variable so the cast in the get(...) method is unneeded? I could not make the " new HashMap<Class<T>, Class<? extends T>>()' work, so either it is impossible or I missed something fundamental :)
Please advise :)
Edit: It turned out that the asSubclass() method on Class did what I wanted :D
Class<?> rawClassFromMap = map.get(interfaceT);
Class<? extends T> implementationT = rawClassFromMap.asSubclass(interfaceT);
It is fine that implementationT is of type "? extends T" as I just need a T object returned.
I like generics. Reminds me of Haskell...
It looks like the goal is something like the "Typesafe Heterogenous Container" described by Josh Bloch in Chapter 5 of Effective Java (item 29). In his case, he's mapping a type (Class<T>) to an (already-instantiated) instance (T).
You can do something similar, using asSubclass instead of cast:
final class Factory
{
private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
<T> void map(Class<T> type, Class<? extends T> impl)
{
map.put(type, impl.asSubclass(type));
}
private <T> Class<? extends T> get(Class<T> type)
{
Class<?> impl = map.get(type);
if (impl == null)
throw new IllegalArgumentException("Unknown type: " + type);
return impl.asSubclass(type);
}
<T> T create(Class<T> type)
throws Exception
{
Class<? extends T> impl = get(type);
Constructor<? extends T> ctor = impl.getConstructor();
return ctor.newInstance();
}
}
I would suggest a Proxy. Here's the Java example.
public interface Bike {
public String getWheels();
public int getSize();
}
public class MountainBike implements Bike {
#Override
public int getSize() {
return 24;
}
#Override
public String getWheels() {
return "Treaded";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: MOUNTAIN").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class CruiserBike implements Bike {
#Override
public int getSize() {
return 26;
}
#Override
public String getWheels() {
return "Smooth";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: CRUISER").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class BikeProxy implements InvocationHandler {
private Object obj;
public static Object newInstance(Object obj)
{
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(),
new BikeProxy(obj));
}
public static <T> T newInstance(String className)
{
try
{
return (T) newInstance(Class.forName(className));
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
public static <T> T newInstance(Class<T> bikeClass)
{
try
{
return (T) java.lang.reflect.Proxy.newProxyInstance(Bike.class.getClassLoader(), new Class[]{Bike.class},
new BikeProxy(bikeClass.newInstance()));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private BikeProxy(Object obj)
{
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object result;
try
{
result = m.invoke(obj, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return result;
}
}
public class ProxyTester
{
public static void main(String[] args)
{
Bike mountainBike = BikeProxy.newInstance(MountainBike.class);
System.out.println(mountainBike);
Bike mountainBike2 = BikeProxy.newInstance(MountainBike.class.getName());
System.out.println(mountainBike2);
Bike cruiserBike = BikeProxy.newInstance(CruiserBike.class);
System.out.println(cruiserBike);
Bike cruiserBike2 = BikeProxy.newInstance(CruiserBike.class.getName());
System.out.println(cruiserBike2);
}
}

Categories