I am not entirely sure what title to put on this problem to get the right minds. This seems like a Java slight of hand but it's only occuring using the Guava Collections2.transform. The transformer is providing a completely different instance of my object during iteration on 'results' then what is contained when 'results' is finally return. And thus, the 'setDateStamp()' doesn't actually appear to work because it's being set on instances that seem to just appear and vanish like a ghost.
When I implement what the logical equivalent of the Collections2.transform() method is doing (commented out code) I get the results I expect. I have stepped through the google code, breakpoints and all, and no where is a new instance being created through any method other than my underlying Function.
I get what their implementation is doing: transform-as-needed. Not complicated. So why the hell doesn't this work?
Here is the code in question along with some debug
@Component
public class SurveyResultToQuestionResults implements Function<SurveyResult, Collection<QuestionResult>> {
@Autowired
private QuestionResultDtoToDomain dtoToDomain;
@Override
public Collection<QuestionResult> apply(@Nullable SurveyResult input) {
Collection<QuestionResult> results = new HashSet<QuestionResult>();
if (input != null) {
// substitute this
// for (QuestionResultDto dto : input.getResults()) {
// QuestionResult result = dtoToDomain.apply(dto);
// results.add(result);
// }
// for this
results = Collections2.transform(input.getResults(), dtoToDomain);
for (QuestionResult result : results) {
long time = input.getSurveyTime().getTime();
Timestamp dateStamp = new Timestamp(time);
result.setDateStamp(dateStamp);
}
}
return results;
}
}
next class
@Component
public class QuestionResultDtoToDomain implements Function<QuestionResultDto, QuestionResult> {
@Override
public QuestionResult apply(@Nullable QuestionResultDto input) {
QuestionResult result = null;
if (input != null)
result = new QuestionResult(input.getAnswerOriginId(),input.getAnswer(),input.getQuestionId());
return result;
}
}
And a test
@RunWith(MockitoJUnitRunner.class)
public class SurveyTransformerTest {
@Spy
private QuestionResultDtoToDomain dtoToDomain = new QuestionResultDtoToDomain();
@InjectMocks
private SurveyResultToQuestionResults surveyResultToQuestionResults = new SurveyResultToQuestionResults();
@Test
public void testSurveyToQuestionResults() throws Exception {
Set<QuestionResultDto> answers = new HashSet<QuestionResultDto>();
answers.add(new QuestionResultDto(17L,"question 2 answer"));
answers.add(new QuestionResultDto(18L,"question 3 answer"));
answers.add(new QuestionResultDto(19L,"question 4 answer"));
SurveyResult result = new SurveyResult(10L,16L,new Date(),answers);
Collection<QuestionResult> qresults = surveyResultToQuestionResults.apply (result);
System.out.println(qresults);
for (QuestionResult qresult : qresults) {
assertNotNull(qresult.getDateStamp());
}
}
}
Debug:
Bad implementation
[QuestionResult{questionResultId=null, answer='question 4 answer', dateStamp=null}, QuestionResult{questionResultId=null, answer='question 2 answer', dateStamp=null}, QuestionResult{questionResultId=null, answer='question 3 answer', dateStamp=null}]
Good implementation:
[QuestionResult{questionResultId=null, answer='question 4 answer', dateStamp=2012-05-17 00:02:18.615}, QuestionResult{questionResultId=null, answer='question 3 answer', dateStamp=2012-05-17 00:02:18.615}, QuestionResult{questionResultId=null, answer='question 2 answer', dateStamp=2012-05-17 00:02:18.615}]
You're surprised that the writes on the new objects aren't writing through to the backing collection?
Collections.transform
doesn't just do the transform "as needed" -- it doesn't store anything at all. That's what "view" means in its documentation. Any time you walk through a Collections2.transform
ed collection, it applies the function again, freshly. Once that for loop in the apply
method is done with result
, that object is gone; never seen again.
If you want to do what you're doing, make an explicit copy of the transformed collection in e.g. an ArrayList
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With