Generics

I've already mentioned that we identified early on that generics would be a problem, and one of the steps we took was to reduce unnecessary and unproductive use of generic types. In fact, we have almost totally eliminated all use of generics in Saxon-defined classes, which was the major problem. That leaves generics in system-defined classes (notably the collection classes such as List<T>) which we can't easily manage without.

In fact, most uses of these classes translate from Java to C# without trouble. But there are still a few difficulties:

Diamond Operators

Java allows you to write List<X> list = new ArrayList<>() (referred to as a diamond operator, though it's not technically an operator). In C# it has to be new ArrayList<X>(). So we need to work out what X is – essentially by applying the same type inferencing rules that the Java compiler applies. The way we do this is by recognising common cases: object instantiation on the right-hand side of an assignment, in a return clause, in an argument to a non-polymorphic method, etc. The logic is quite complex, and it catches perhaps 95% of cases. The remainder are handled by changing the Java code: either by introducing a variable, or by adding the type redundantly within the diamond.

XSLT template rules really come into their own here. We handle about a dozen patterns where the type of the parameter can be inferred, and each of these is represented by a template rule. As we get smarter or discover more cases, we can simply add more template rules. Here's an example of one of the rules:

<xsl:template match="*[@nodeType='ReturnStmt']
        [ancestor::member[1]/type/@RESOLVED_TYPE]/*">
   <xsl:variable name="type" 
                 select="ancestor::member[1]/type/@RESOLVED_TYPE" 
                 as="xs:string"/>
   <xsl:value-of select="f:extract-type-arguments($type)"/>
</xsl:template>

This rule detects a diamond operator appearing in a return statement (the rule appears in a module with default mode diamond, which is only used to process expressions that have already been recognised as containing a diamond operator). It finds the ancestor method declaration (ancestor::member[1]), determines the declared type of the method result, and inserts that into the C# code as the type parameter in place of the diamond operator.

Wildcards

The Java wildcard constructs <? extends T> and <? super T> have no direct equivalent in C#. The way we handle these depends on where they are used. The default action of the converter is just to replace them with <T>, which often works. But in class and method declarations we generate a C# where clause to constrain the type bounds, so

public class GroundedValueAsIterable<T extends Item> 
     implements Iterable<T> {...}

becomes

public class GroundedValueAsIterable<T> : IEnumerable<T> 
     where T : Item {...}

One issue we face is that the default type Object in Java is less all-embracing than the object type in C#: the former does not include primitive types such as int or double, the latter does. This means that where the required type is Object, the supplied value can be null; but this is not so in C#, because primitive types do not allow a null. This permeates the design of collection classes. Often the solution is to constrain the C# class to handle reference types only, using the clause where T : class.