-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
17, 18, 19
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
ImmutableCollections.ListItr is an implementation of a ListIterator used in immutable lists, and it's also used as the return value of iterator() calls.
The implementation holds a boolean, isListIterator, that is true if the instance was created for a listIterator() call, false if it was for an iterator() call. It is used to throw UnsupportedOperationException() in case it was obtained as the result of a regular iterator().
This is a problem for multiple reasons. First, the boolean would normally be unnecessary given that the return of the iterator() method is an Iterator, and therefore the ListIterator specific methods are not visible. However, a consumer could later do an instanceof ListIterator check in a given object, that would yield true, and therefore assume the class is a proper implementation of ListIterator. Even though the spec of ListIterator doesn't contemplate that these methods throw UnsupportedOperationException, they would.
Suggestion to remove the boolean and checks for isListIterator.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Obtain an immutable List instance, for example by using List.of(E...).
- Obtain an Iterator over that list by calling list.iterator()
- Check for instanceof ListIterator (a check that could perfectly be in a library that you pass this too) and safely cast it to that
- Call one of the following methods on the ListIterator: hasPrevious(), previous(), nextIndex(), previousIndex()
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The methods return an appropriate value and move the cursor if applicable.
ACTUAL -
An UnsupportedOperationException with no detail message is thrown.
---------- BEGIN SOURCE ----------
var list = List.of(1, 2 , 3, 4);
var iterator = list.iterator();
if (iterator instanceof ListIterator<Integer> listIterator) {
listIterator.hasPrevious(); // UOE
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Calling listIterator() on the List instance
FREQUENCY : always
ImmutableCollections.ListItr is an implementation of a ListIterator used in immutable lists, and it's also used as the return value of iterator() calls.
The implementation holds a boolean, isListIterator, that is true if the instance was created for a listIterator() call, false if it was for an iterator() call. It is used to throw UnsupportedOperationException() in case it was obtained as the result of a regular iterator().
This is a problem for multiple reasons. First, the boolean would normally be unnecessary given that the return of the iterator() method is an Iterator, and therefore the ListIterator specific methods are not visible. However, a consumer could later do an instanceof ListIterator check in a given object, that would yield true, and therefore assume the class is a proper implementation of ListIterator. Even though the spec of ListIterator doesn't contemplate that these methods throw UnsupportedOperationException, they would.
Suggestion to remove the boolean and checks for isListIterator.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Obtain an immutable List instance, for example by using List.of(E...).
- Obtain an Iterator over that list by calling list.iterator()
- Check for instanceof ListIterator (a check that could perfectly be in a library that you pass this too) and safely cast it to that
- Call one of the following methods on the ListIterator: hasPrevious(), previous(), nextIndex(), previousIndex()
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The methods return an appropriate value and move the cursor if applicable.
ACTUAL -
An UnsupportedOperationException with no detail message is thrown.
---------- BEGIN SOURCE ----------
var list = List.of(1, 2 , 3, 4);
var iterator = list.iterator();
if (iterator instanceof ListIterator<Integer> listIterator) {
listIterator.hasPrevious(); // UOE
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Calling listIterator() on the List instance
FREQUENCY : always