Features C# has that Java should

James Vickers
The Startup
Published in
7 min readJun 7, 2020

Is it true that C# is just like Java? After working in Java for the last few years and coming back to C# again, there are lots of features I would be disappointed to give up. These are all things that are in the latest version of C# (8) but to my knowledge are not in the latest version of Java (14) at the time of this writing. In no particular order:

  • Tuples
  • Function types with more arguments
  • Default arguments
  • Named arguments
  • Null coalescing operators
  • Better casting
  • Extension methods
  • Properties
  • Expression-bodied function members
  • Interpolated and literal strings
  • Exception filters
  • Out parameters
  • Object initializers

…and two things Java has that I wish C# did:

  • Immutable local variables
  • Things can be overridden by default

Tuples

How many times have you seen a custom defined Pair<L, R> class in Java? The Apache Commons library defines Pair and Triple<L, M, R> but if you wouldn’t bring it in otherwise that’s a lot of extra things to add just for these types. C# has a built-in Tuple class that supports up to 7 values. The closest thing in Java is the java-tuples library which gives a different name to each arity of tuple such as Pair, Triplet, Quartet…and doesn’t have the features of C# tuples such as destructuring, declaring methods as returning tuples with names for each item, and more:

Function types with more arguments

Java 8 added classes that define Functions that can be passed as arguments or created using a lambda function or method reference syntax. However there are only a few arities built-in: Functions that take one or two arguments and return nothing (Consumer and BiConsumer), and functions that take one of two arguments and return something (Function and BiFunction). If you want to pass around a function that takes more than two arguments, you might end up defining something like this as I did quite a bit:

C# has built-in types for functions that take up to 16 arguments and return nothing or something (Action<T1, T2…> and Func<T1, T2…>).

Another thing I found really disappointing about Java Function types is that they can’t throw checked exceptions. Thus, they can’t be used with code that does throw checked exceptions which hampers or restricts their use quite often, for example when working with files. C# does not have checked exceptions, so its Func/Action types can do arbitrary operations.

Default arguments

Defining a function or constructor in Java and some of the arguments have reasonable defaults? Your only hammer is to define overloads that call other signatures with those defaults. This gets out of hand quickly as the number of signatures grow as O(2^n) for n optional arguments:

Another approach I’ve been for Java is to only define one signature, but allow special values such as null for objects or -1 for numbers and check for them in the body of the method to use other values instead; this is confusing for callers. C# has default arguments which are super easy to use:

The default value for an argument must be a compile-time constant (or use default). I’m hoping in the future they support lambda expressions as well, that would be very useful sometimes.

Named arguments

Calling a method with several parameters of the same type? Better be careful you get the order right in Java:

In C#, you can attach the names to each argument to ensure they’re correctly specified:

Notice the arguments were passed in a different order. This code will still be correct as long as the names don’t change, in which case your code won’t compile. As a bonus, this often adds clarity when reading the calling code; I usually include the argument name when passing a boolean flag.

Null coalescing operators

Dereferencing null pointers is a common source of errors. Avoiding that in Java costs some verbosity unless you opt for a static analysis tool like the Nullness Checker. Typically things are done like this:

C# has null-coalescing operators for safely traversing objects that may be null (‘?’) and giving defaults when things are allowed to be null (‘??’):

Better casting

Inspecting the type of something and casting in Java is pretty verbose. A common example is overriding equality:

C# has operators that make this easier. Here’s an example using the is operator:

Edit: Seliba pointed out that Java 14 adds JEP 305 which supports basically this same thing, although in ‘preview’ status (so it may not be permanent).

Extension methods

C# extension methods allow you to add methods to existing types instead of defining functions that take the type as a parameter. An example:

Properties

C# properties are basically just a convenient shorthand for getters and setters, but once you get used to them the old style seems verbose. In Java:

In C# using properties:

This example uses the shorthand get/set which just does the basic thing you expect, but any/either of those can also have a body implementation. They can also have different access modifiers, such as { get; protected set; }.

Expression-bodied function members

I use this one constantly! It’s concise and I find using it is a good way to strive for short methods. It can be used in places such as constructors, methods, and properties. Some examples:

Interpolated and literal strings

Combining variables or expressions into strings in Java lacks readability and is generally annoying:

C# has interpolated strings, pretty similar to Python F-strings:

C# also has literal strings, which are convenient for example when the string has escape characters in it. Example from Java trying to put ‘\’ characters into a string, which requires two of them for every one because of escaping:

Same example in C# using a literal string instead:

Exception filters

Suppose you’re calling a method which can throw an exception that includes a status code in it, and you only want to catch the exception for a particular status code. In Java that looks something like:

C# has exception filters for occasions like this:

Out parameters

When parsing a number from a string, error handling in Java is a bit of a pain:

The difficulty here is that Integer.parseInt returns an integer, but it throws an exception if the string can’t be parsed as one. The corresponding library methods in C# make use of out parameters, such as TryParseInt:

C# can define TryParseInt to take the resulting int as an out parameter which is assigned on success and also return a bool that indicates a success rather than throwing an exception to indicate failure. This pattern of ‘Try’ is also used for looking up something in a Dictionary via TryGetValue. Returning a bool and assigning an out parameter provide a concise way to call these ‘Try’ methods in a conditional statement.

Object initializers

Suppose you are creating an object that has mutable properties and only a no-arg constructor. This is pretty common, for instance when using some frameworks such as ORM’s. This is what you must do in Java for such an object:

C# has object initializers, which allows you to construct the object and assign it’s fields as you wish in a more compact and readable fashion:

This may seem like a small difference until you are creating anonymous instances like this for passing as arguments, for example.

Things Java has that C# should

Immutable local variables

C# has readonly, but that can only be used on fields; it also has const, but that can only be on constant expressions (e.g. strings, numbers, compile-time known things). Java has the final keyword which can be applied to any variable.

Things can be overridden by default

C# properties, methods, and classes are not overridable unless marked as virtual, which can inhibit your ability to mock their behavior in unit tests. This often means classes you don’t control are not mockable since virtual is often omitted from the places you want to mock. I have been frustrated at not being able to create mocks of most objects from WinForms and DevExpress, for example. Java methods and classes are overridable by default which makes them mockable in unit tests by default.

Conclusion

This is by no means a complete survey of the features C# has over Java; hopefully the comments will have even more examples of things I left out such as nullable and anonymous types, IEnumerable, and better generics. I am certainly not expert at C# so apologies for the details I missed or got wrong.

Seeing the features of C# 8 and those coming in C# 9 in comparison to Java has had me wondering: Is the Java language stagnating? What makes it more popular than C# at this point? Performance? Momentum? Familiarity? Distrust of Microsoft?

I’m not trying to hate on Java. But the next time you’re starting starting a new Java project, consider: what are you missing out on by not using C# instead?

--

--