Details
-
Bug
-
Status: Closed
-
Major
-
Resolution: Resolved
-
None
-
None
-
None
-
None
Description
A recent change to Execution changed withFilter() and friends so they introduce their own type parameters:
<ArgumentT, ReturnT, AggregatorT> Execution<ArgumentT, ReturnT, AggregatorT> withFilter( Set<?> filter);
This eliminates a compile error over in FunctionRetryTestBase where chaining was happening:
final Execution<Integer, Long, List<Long>> execution; switch (executionTarget) { case REGION: execution = FunctionService.onRegion(clusterStartupRule.getClientCache().getRegion(regionName)) .setArguments(200);
Unfortunately though, this change introduces a "hole" in the typing so that the client (of Execution) is able to produce instances parameterized on arbitrary types. The compiler is happy with just about anything, but runtime exceptions (ClassCastException) will ensue.
Here is an example of code that compiles https://gist.github.com/Bill/5e2ad2ba4b60461dd0af8adfa4adc91b:
final Execution<String, String, String> execution = FunctionService.onRegion(null); //Add a string argument, which appears to be typesafe final Execution<String, String, String> addedArgument = execution.setArguments("hello"); //But wait! If we call withFilter, we get to change the type of Execution with no warning //or casts. We won't see the class cast exceptions until runtime. final Execution<Integer, Integer, Integer> execution1 = execution.withFilter(Collections.singleton(5));
A hint at the problem is that IntelliJ inspections highlights the new type parameters on e.g. the withFilter() method indicating that the type parameters on the method are hiding the type parameters on the class. This is an indication that even though the names are the same—the type parameters to the method are completely different from the ones on the class. There is no enforced correspondence. The compiler gives you complete freedom here.