Skip to content

Review and correct the Java-to-C# type mappings (#134) - #181

Merged
paulirwin merged 1 commit into
masterfrom
fix/134-type-mappings
Aug 19, 2026
Merged

Review and correct the Java-to-C# type mappings (#134)#181
paulirwin merged 1 commit into
masterfrom
fix/134-type-mappings

Conversation

@paulirwin

Copy link
Copy Markdown
Owner

Fixes #134.

Interfaces were bound to concrete types

The issue's example — Map<K, V> mapping to Dictionary<K, V> instead of IDictionary<K, V> — turned out to be one of several. Converting a probe file showed variables declared against a Java collection interface were losing the abstraction the Java source chose, even though ListIList was already correct.

Map now maps to IDictionary and Set to ISet, with Collection, Comparable, Comparator, Iterable, SortedMap/SortedSet and the Navigable variants mapped alongside them.

...which required mapping the concrete types too

Fixing only the interfaces would have made output worse, since interfaces are exactly what you cannot instantiate. new HashMap<>() was previously emitting new HashMap() — a type that does not exist in .NET — because HashMap, LinkedHashMap, LinkedHashSet, TreeMap and TreeSet had no entries at all. Those are added.

Map.put is also lowered to an index assignment, the way List.set already was. This is not strictly a type mapping, but the integration test would not compile without it: a converted IDictionary had no usable way to store into itself. It reuses the existing List.set lowering, which already discards the same return value.

Other commonly-used types filled in

Boxed primitives (Character, Double, Short, and Bytesbyte, as Java's byte is signed), BigDecimal, StringBuffer, Closeable, and the common exceptions: Throwable, ClassCastException, NumberFormatException, IndexOutOfBoundsException, ArrayIndexOutOfBoundsException, NoSuchElementException, OutOfMemoryError, StackOverflowError, InterruptedException, CloneNotSupportedException.

Deliberately left unmapped: CharSequence, Runnable, Void

All three were added and then removed. CharSequencestring broke an existing test: class Foo implements CharSequence became : string, which does not compile because string is sealed. RunnableAction and Voidvoid (illegal in Future<Void>) fail the same way.

The mapping table is position-blind — it cannot tell a variable declaration from a base-type list — so each of these would fix some conversions while breaking others. Handling them needs position-aware conversion rather than a table entry.

Testing

All 415 tests pass. New unit tests cover the collection, simple-type and exception mappings; a new CollectionTypeMappings.java integration resource compiles and runs the generated C# rather than only asserting on the conversion text. To support that, the integration harness now mirrors the CLI's default usings and references System.Collections.dll (where SortedDictionary/SortedSet live).

Pre-existing bugs found but not fixed

Both are out of scope here; happy to file them separately.

  • Diamond operator drops type arguments: new HashMap<>() converts to new Dictionary(), losing <string, int>. The new test resource uses explicit type arguments to work around this.
  • Qualified type names pass through unconverted: TypeNameParser handles simple identifiers only, so Map.Entry<K, V> is left as-is. Relatedly, java.* imports emit unusable usings such as using Java;.

🤖 Generated with Claude Code

Variables declared against a Java collection interface were being bound to a
concrete .NET type: Map became Dictionary and Set became HashSet, losing the
abstraction the Java source chose. Map now maps to IDictionary and Set to ISet,
matching how List already mapped to IList, and Collection, Comparable,
Comparator, Iterable, SortedMap/SortedSet and their Navigable variants are
mapped alongside them.

That change only works if the concrete implementations are mapped too, since
those are what `new Foo<>()` resolves to, so HashMap, LinkedHashMap,
LinkedHashSet, TreeMap and TreeSet are added. Java's Map.put is also lowered to
an index assignment, the way List.set already was; without it a converted map
declaration has no usable way to store into itself.

Also fills in the boxed primitives (Character, Double, Short, Byte -- to sbyte,
as Java's byte is signed), BigDecimal, StringBuffer, Closeable, and the common
exceptions: Throwable, ClassCastException, NumberFormatException,
IndexOutOfBoundsException and friends.

CharSequence, Runnable and Void are deliberately left unmapped. The mapping
table cannot see what position a type appears in, and each of those is valid as
a base type in Java but not as its natural C# counterpart -- string is sealed,
and `Future<void>` does not compile.

The integration harness now mirrors the CLI's default usings and references
System.Collections.dll, so the new sample compiles and runs the generated C#
rather than only asserting on the conversion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@paulirwin
paulirwin merged commit 6979f38 into master Aug 19, 2026
5 checks passed
@paulirwin
paulirwin deleted the fix/134-type-mappings branch August 19, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect type mappings

1 participant