If we could have the best of both worlds between C# and Java, what would that look like?

Most C# developers will be quick to tell you that with more frequent updates, C# has everything Java has and more. C# had Generics and Lambdas long before we got them in Java, but there are still major components in Java that we don’t see in C#.

We researched the top features that Java developers miss in C# and chose 5 of them to list here. Some of them offer clear benefits to Java users, while others are surrounded by controversy.

Let’s take a look.

1. Checked Exceptions

Java divides exceptions into Checked and Unchecked. Checked exceptions are conditions that are checked at compile time. Certain methods that are likely to throw an exception must be handled using a try/catch block or otherwise must specify the exception with a throws statement. Because these are issues that commonly occur, Java requires us to provide the logic to handle them at compile time. Without one of these required statements the code will fail to compile.
This is simply meant to encourage developers to write more robust software.

On the other hand, not only does C# not have checked exceptions, the architects purposefully didn’t include the feature. The debate over whether having checked exceptions is a benefit or a drawback has gone on for close to 2 decades now and is still nowhere near settled.

For Microsoft Chief Architect Anders Hejlsberg, there are two issues with Java’s implementation of checked exceptions, versioning and scalability. The trouble with creating a new version of a method, he says, is that you may be adding new features that also introduce a new exception. If you do introduce a new exception, adding it to the throws clause may break the code entirely because the caller of the method most likely doesn’t handle that exception. In terms of scalability, when systems become very large, common practice is to simply write throws Exception everywhere or to write in empty try catch blocks that we’ll “come back and deal with later.” We showed this when we looked at over 600,000 Java projects on Github and Sourceforge and found that 20% of catch blocks were empty and most of these exceptions were ignored in the end.

2. Non-Static Inner Classes

Both Java and C# have nested classes, but what Java does differently is dividing nested classes into two main categories. In each language, you find static nested classes, which is a static member of the outer class and doesn’t have access to instance variables or methods from the outer class. These nested classes can be called without first initiating the outer class.

In Java, though, there is another type of nested class called inner classes, which are non-static. These classes include member, local and anonymous inner classes as shown in the chart below.

Source: tutorialspoint.com. This image shows all types of Nested classes in Java, whereas C# has only Static Nested classes as shown on the right-hand side.

Member inner classes are simply non-static classes within a larger class but outside a method. To create an instance of a member inner class, you must first instantiate the outer class to which it belongs. These classes can then be used to access private or public instance variables or methods from the outer class. Method local inner classes are similar but are contained within a method and can only be instantiated within the scope of the method itself.

Lastly, anonymous classes are, to put it simply, inner classes that are created without being given a name. They work similarly to local inner classes, but are declared and instantiated at the same time, making them good to use in place of a local inner class that you want to use only once. In Java, anonymous classes are essentially used to extend the parent class. C# doesn’t have a direct equivalent, but a similar effect can be achieved with the use of events and delegates.

One benefit of (non-static) inner classes is that they can be made private, unlike classes in general, so that they can only be accessed by an object from the parent class. Other advantages of using inner classes in Java include accessing all members of the outer classes (including private members), writing more maintainable code and optimizing how the code is written.

3. Final Keyword

Polymorphism is one of the defining properties of Object-Oriented Languages, and it wouldn’t be possible without virtual methods. A virtual method is one whose function can be overridden by any class that inherits it. In Java, every method is assumed to be virtual by default and can be made non-virtual using the final keyword. Conversely, in C#, all methods are non-virtual by default and so a directly equivalent keyword would have no use.

In Java, the final keyword can be applied to a variable, method or class. In each instance, the keyword has similar consequences. A final variable will act as a constant and its value will be fixed. A final method can’t be overridden, and a final class can’t be extended.

To prevent a class from being inherited from in C#, you can use the sealed keyword. In the case of non-class fields, there are two different keywords that can be used to prevent modification. Readonly is to be used for runtime constants, while const is used for compile time constants. Basically, when using the const keyword the value of the constant must be explicitly stated and is evaluated at compile time, whereas the value of a readonly field is assigned by the constructor but is not evaluated until runtime.

4. Covariant Method Return Types

Although this difference is subtle and use cases are fairly uncommon, the existence of covariant return types in Java can save you from needing to create new methods.

Basically, in C#, a method in a subclass that overrides a method in the base class has to match the name, argument type and return type of the method in the base class. In this case, the overriding method is invariant with respect to return type, and if you want to narrow the return type you have to create a new method.

The following code snippet in Java narrows the return type of the Clothes method when overriding it in the Pants subclass, so Pants.newClothes returns Jeans type which is a subtype of the Clothes method. Generally, this is a more ideal way to work with inheritance hierarchies.

5. Enums as Specialized Classes

There was a time when developers bemoaned the lack of enums in Java, and now it’s one of the things that is more commonly thought to have been done better in Java. Enums in C# closely resemble those in C++ and are essentially seen as glorified integral types. A good example of an enum in C# is one which includes the days of the week as members:

By default, the value of the first list item is 0 and the value of each successive item increases by 1 (i.e. Sat=0, Sun=1, Mon=2, etc.)

Although it didn’t happen until several years later, Java did something very different when they introduced enums as specialized classes.

Enums in Java are said to be like specialized classes because they are, in fact, “full-fledged classes”. Unlike their C# counterparts, they can take arbitrary methods and fields as inputs making them much more powerful. Take this example, for instance, which includes methods and some basic logic in Java:

6. Tooling Ecosystem

Beyond the language differences between Java and C#, each has its own runtime environment (Java runs on the JVM and C# uses Microsoft’s CLR). With different runtime environments, each language has a different set of production monitoring tools available to use.

OverOps is the only tool that shows developers the complete source code and variable state across the entire call stack for every error and exception in production. It’s currently only available for JVM languages like Java, but .NET compatibility is coming in the next few months. For more information and to join the waiting list for our .NET Beta click here, or if you’re a Java developer request a free demo of the product here.

View the full stack trace and variable state of every error and exception in your OverOps dashboard

Final Thoughts

In writing this piece, we were reminded again that individual language features in Java and C# are not the defining properties of their respective ecosystems. The two languages are in a perpetual game of leapfrog in respect to adding features, one does something first and the other is usually soon to follow.

As we said in a previous post, we don’t want to get stuck in the middle of the never-ending argument over which language is better. Each has its own advantages and disadvantages, and other external factors to take into consideration. This is just us pointing out some Java features that are lacking in C#. Did we miss a feature that you want to see added to C#? Let us know in the comments below!

email
Tali studied theoretical mathematics at Northeastern University and loves to explore the intersection of numbers and the human condition. In her free time, she enjoys drawing and spending time with animals.
  • Robert Friberg

    I’m deep into both C# and Java but wasn’t aware of the covariant method return types in java, that is awesome.

    • Tali Soroker

      That’s great to hear! Have you seen our post looking at C# features that aren’t in Java? If not, you can check it out here: http://bit.ly/2tUU6nz

  • Damien Guard

    You can achieve results similar to “Enum as Specialized Classes” by defining extension methods that take the Enum.

    Alternatively you can define a class and expose a singleton instance of each on static members to get something that works very similar although you’ll have some boilerplate to write and maintain.

    Can you explain what’s missing in #3 a bit more? It looks like C# has the same functionality but split between different keywords or am I missing something? Methods are non-virtual by default for performance (speed/memory)

  • Stern SternR

    C# has the notion of covariance:
    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

    Also, its true C# doesn’t require you to declare throws on each method, but you can force the compiler to use the exception xml attribute as compile error and practically get the same result:
    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/exception

    • gacl

      This article was specifically referring to return types in overloaded functions. Of course, C# has covariant casting in general. That article btw, refers to declaration site type variance with generics, which is a big feature that Java is missing relative to C# and Scala. Java is actively adding that with JEP 300, but it’s not available yet.

  • Binh Thanh Nguyen

    Thanks, nice post

  • Jacob Zimmerman

    I agree with covariant return types and awesome enums, but the first 3 were things that some JVM have actually worked hard to remove, and I agree with their decision. As for tooling… meh.

  • koshelew@live.com

    Java was good when it started but then stagnated, especially after falling into the hands of Oracle. When I think I will have to give up C# features as powerful as first class Expression Trees https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/ , async/await https://docs.microsoft.com/en-us/dotnet/csharp/async or even something as basic as inline string interpolation https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interpolated-strings , I realize I will never go back to Java. Kotlin does look promising though.

    • gacl

      The Java language itself is supposed to be a more conservative, stable language focused on backward compatibility. If you want more cutting edge language syntax and can tolerate less backwards compatibility, you are supposed to use something like Scala or Clojure or Kotlin. BTW, Scala had features like string interpolation ages before C#.

  • Johny Herl

    The Spring Framework is a lightweight framework for developing Java enterprise applications. It provides high performing, easily testable and reusable code. Spring handles the infrastructure as the underlying framework so that you can focus on your application.Spring is modular in design, handling and linking of individual components so much easier. Spring implements Model View Container(MVC) design pattern.
    Spring mvc hello world example

  • gacl

    Checked Exceptions are terrible, every major newer language avoided them. 2,3,4 are quite minor. #5 is the one legitimate nice syntax feature in Java missing from C#, although, I’d prefer full featured ADT/GADT.

    The big reason to use Java is the ecosystem, not the language syntax. With Scala, you could list big syntax features that C#/Java developers want.

    With Java, I’d actually cite syntax simplicity, and lack of multiple redundant features, as the main advantage of the Java language over C#. C# added more features, faster than Java, and many are great. But sometimes, they added the wrong feature in hindsight. For example, “out” parameters were a poor substitute for the ability to return an anonymous tuple. C# 7 got proper anonymous tuples, but “out” parameters will forever be part of the syntax. Or delegates were basically superseded by proper lambdas, but the language will forever have both. Or “properties”: almost every version of C# introduced new syntax option for class properties, they are all still supported, yet they still don’t a simple record/namedtuple type which is what devs want and has been in just about every other language except Java for ages. Or consider C# readonly vs const vs read-only properties all having different syntax: what a mess!