FULL PRODUCT VERSION :
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10 Home v1709 build 16299.192
EXTRA RELEVANT SYSTEM CONFIGURATION :
Eclipse Platform
Version: Oxygen.1a (4.7.1a)
Build id: M20171009-0410
A DESCRIPTION OF THE PROBLEM :
Streaming a collection, calling a terminal operation which returns another collection, then initiating another stream on that results in the type information being lost. This problem is resolved when the intermediate collection is assigned to a variable and the stream is started again on the new variable, but chaining the two doesn't work despite the code being valid and otherwise unchanged. Perhaps this is a bug with the compiler's ability to handle longer and/or more complex method chains involving generics, streams and automatic type inference of generic method parameters.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A static inner class with two fields (let's say of types K and V). Make a static method which takes a collection of this class and returns a Map<K, V>. This is done by taking the collection as a parameter, streaming it, calling the 4 arg collect method, with some imperative code within the parameters of the collect arguments. So far this works and you can return. But as soon as you chain on entrySet().stream() on this and do some further processing but in the end convert to a map conforming to the return type, the type information gets lost in the entire chain, with everything becoming an Object.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The method should compile.
ACTUAL -
The method reports compile errors despite the code being correct.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
In order of appearance:
Type mismatch: cannot convert from Object to Integer
Type mismatch: cannot convert from element type Map.Entry<Object,Object> to Map.Entry<String,Integer>
Type mismatch: cannot convert from Object to Integer
The method sorted(Comparator<? super Map.Entry<Object,Object>>) in the type Stream<Map.Entry<Object,Object>> is not applicable for the arguments (Comparator<Map.Entry<Object,Comparable<? super Comparable<? super V>>>>)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public static class UnsatisfiedOclConstraint {
public final String constraintName;
public final EObject element;
public UnsatisfiedOclConstraint(String constraintName, EObject element) {
this.constraintName = constraintName;
this.element = element;
}
public static Map<String, Integer> sortUnsatisfiedConstraintsBySize(Collection<UnsatisfiedOclConstraint> unsatisfiedConstraints) {
return unsatisfiedConstraints
.stream()
.collect(HashMap::new,
(results, uc) -> {
Integer countSoFar = results.get(uc.constraintName);
results.put(uc.constraintName, countSoFar != null ? countSoFar+1 : 1);
},
(total, intermediate) -> {
for (Map.Entry<String, Integer> entry : intermediate.entrySet()) {
String key = entry.getKey();
int intermediateCount = entry.getValue();
Integer totalCount = total.get(key);
total.put(key, totalCount != null ? totalCount+intermediateCount : intermediateCount);
}
}
)
.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, LinkedHashMap::new)
);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Assigning the intermediate result into a variable ("unsorted", in this case) fixes this issue, like so:
public static Map<String, Integer> sortUnsatisfiedConstraintsBySize(Collection<UnsatisfiedOclConstraint> unsatisfiedConstraints) {
Map<String, Integer> unsorted = unsatisfiedConstraints
.stream()
.collect(HashMap::new,
(results, uc) -> {
Integer countSoFar = results.get(uc.constraintName);
results.put(uc.constraintName, countSoFar != null ? countSoFar+1 : 1);
},
(total, intermediate) -> {
for (Map.Entry<String, Integer> entry : intermediate.entrySet()) {
String key = entry.getKey();
int intermediateCount = entry.getValue();
Integer totalCount = total.get(key);
total.put(key, totalCount != null ? totalCount+intermediateCount : intermediateCount);
}
}
);
return unsorted
.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, LinkedHashMap::new)
);
}
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10 Home v1709 build 16299.192
EXTRA RELEVANT SYSTEM CONFIGURATION :
Eclipse Platform
Version: Oxygen.1a (4.7.1a)
Build id: M20171009-0410
A DESCRIPTION OF THE PROBLEM :
Streaming a collection, calling a terminal operation which returns another collection, then initiating another stream on that results in the type information being lost. This problem is resolved when the intermediate collection is assigned to a variable and the stream is started again on the new variable, but chaining the two doesn't work despite the code being valid and otherwise unchanged. Perhaps this is a bug with the compiler's ability to handle longer and/or more complex method chains involving generics, streams and automatic type inference of generic method parameters.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A static inner class with two fields (let's say of types K and V). Make a static method which takes a collection of this class and returns a Map<K, V>. This is done by taking the collection as a parameter, streaming it, calling the 4 arg collect method, with some imperative code within the parameters of the collect arguments. So far this works and you can return. But as soon as you chain on entrySet().stream() on this and do some further processing but in the end convert to a map conforming to the return type, the type information gets lost in the entire chain, with everything becoming an Object.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The method should compile.
ACTUAL -
The method reports compile errors despite the code being correct.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
In order of appearance:
Type mismatch: cannot convert from Object to Integer
Type mismatch: cannot convert from element type Map.Entry<Object,Object> to Map.Entry<String,Integer>
Type mismatch: cannot convert from Object to Integer
The method sorted(Comparator<? super Map.Entry<Object,Object>>) in the type Stream<Map.Entry<Object,Object>> is not applicable for the arguments (Comparator<Map.Entry<Object,Comparable<? super Comparable<? super V>>>>)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public static class UnsatisfiedOclConstraint {
public final String constraintName;
public final EObject element;
public UnsatisfiedOclConstraint(String constraintName, EObject element) {
this.constraintName = constraintName;
this.element = element;
}
public static Map<String, Integer> sortUnsatisfiedConstraintsBySize(Collection<UnsatisfiedOclConstraint> unsatisfiedConstraints) {
return unsatisfiedConstraints
.stream()
.collect(HashMap::new,
(results, uc) -> {
Integer countSoFar = results.get(uc.constraintName);
results.put(uc.constraintName, countSoFar != null ? countSoFar+1 : 1);
},
(total, intermediate) -> {
for (Map.Entry<String, Integer> entry : intermediate.entrySet()) {
String key = entry.getKey();
int intermediateCount = entry.getValue();
Integer totalCount = total.get(key);
total.put(key, totalCount != null ? totalCount+intermediateCount : intermediateCount);
}
}
)
.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, LinkedHashMap::new)
);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Assigning the intermediate result into a variable ("unsorted", in this case) fixes this issue, like so:
public static Map<String, Integer> sortUnsatisfiedConstraintsBySize(Collection<UnsatisfiedOclConstraint> unsatisfiedConstraints) {
Map<String, Integer> unsorted = unsatisfiedConstraints
.stream()
.collect(HashMap::new,
(results, uc) -> {
Integer countSoFar = results.get(uc.constraintName);
results.put(uc.constraintName, countSoFar != null ? countSoFar+1 : 1);
},
(total, intermediate) -> {
for (Map.Entry<String, Integer> entry : intermediate.entrySet()) {
String key = entry.getKey();
int intermediateCount = entry.getValue();
Integer totalCount = total.get(key);
total.put(key, totalCount != null ? totalCount+intermediateCount : intermediateCount);
}
}
);
return unsorted
.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, LinkedHashMap::new)
);
}