I want to limit types mapped by my ModelMapper in case when I'm mapping from dto model to entity.
I've defined ModelMapper like this:
var modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT)
.setPreferNestedProperties(false)
.setFullTypeMatchingRequired(true)
.setPropertyCondition(
Conditions.or(Conditions.isType(String.class),
Conditions.or(Conditions.isType(Integer.class),
Conditions.or(Conditions.isType(Long.class)))));
Unfortunatly it doesn't work. During compilation I got an error:
method or in class org.modelmapper.Conditions cannot be applied to given types;
What is wrong? Is there any other method to do that?
I want to limit types mapped by my ModelMapper in case when I'm mapping from dto model to entity.
I've defined ModelMapper like this:
var modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT)
.setPreferNestedProperties(false)
.setFullTypeMatchingRequired(true)
.setPropertyCondition(
Conditions.or(Conditions.isType(String.class),
Conditions.or(Conditions.isType(Integer.class),
Conditions.or(Conditions.isType(Long.class)))));
Unfortunatly it doesn't work. During compilation I got an error:
method or in class org.modelmapper.Conditions cannot be applied to given types;
What is wrong? Is there any other method to do that?
Share Improve this question asked Feb 6 at 11:47 OlekOlek 35911 silver badges24 bronze badges1 Answer
Reset to default 2The error is caused when Java resolves the actual type arguments of the isType
calls to determine the corresponding ones for the or
call. This is an issue with the usage of Java generics in this specific case.
As we can see in the signature of or
method it accepts two Condition
s, which have the same type parameters (ie S
and D
). But isType
returns wildcard type parameters (ie both are ?
). Wildcard type parameters here mean that any type is acceptable. But or
expects two arguments with the same type parameters. Since type parameters of both arguments to or
can be anything (as returned by the two isType
calls) then they can be different too, hence the error.
For example, the following code:
public class Main {
private static interface Condition<A, B> {
}
private static <S, D> Condition<S, D> or(final Condition<S,D> condition1,
final Condition<S,D> condition2) {
return null; //Doesn't matter to have an actual instance here.
}
public static void main(final String[] args) {
final Condition<?, ?> c1 = null, //Doesn't matter to have an actual instance here.
c2 = null; //Doesn't matter to have an actual instance here.
or(c1, c2); //Compilation error here.
}
}
... produces the following full compilation error message:
method or in class Main cannot be applied to given types; required: Condition,Condition found: Condition,Condition reason: inference variable S has incompatible equality constraints CAP#3,CAP#1 where S,D are type-variables: S extends Object declared in method or(Condition,Condition) D extends Object declared in method or(Condition,Condition) where CAP#1,CAP#2,CAP#3,CAP#4 are fresh type-variables: CAP#1 extends Object from capture of ? CAP#2 extends Object from capture of ? CAP#3 extends Object from capture of ? CAP#4 extends Object from capture of ?
As you can see for example CAP#1
extends Object
, but so does CAP#3
. The same is true for CAP#2
vs CAP#4
. Since all of these extend Object
, but no other constraint is in place, then they can be different subclasses of Object
, but or
requires the same S
for both its arguments, and the same D
for both its arguments.
For example CAP#1
can be (say) an Integer
and CAP#3
(say) a String
. So while they would satisfy the wildcard, they cannot satisfy S
(which is required to be the same for both or
's arguments) since Integer
and String
are not the same.
Also, the last call to or
is missing its second argument entirely, but that's not the error questioned here.
I am not familiar with ModelMapper
, but (according to its documentation and source code) it seems that the following method may help to workaround the issue:
public static <S, D> Condition<S, D> isAnyTypeOf(final Class<? extends S>... sourceTypes) {
final List<Class<?>> testTypes = Collections.unmodifiableList(Arrays.asList(sourceTypes.clone()));
return (final MappingContext<S, D> context) -> {
final Class<S> srcType = context.getSourceType();
return testTypes.stream().anyMatch(testType -> testType.isAssignableFrom(srcType));
};
}
And use it like:
var modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT)
.setPreferNestedProperties(false)
.setFullTypeMatchingRequired(true)
.setPropertyCondition(isAnyTypeOf(String.class, Integer.class, Long.class));