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

The perfect programming language doesn’t exist. I hope we can agree on that, if nothing else. New languages are often developed in response to the shortcomings of another, and each is inevitably stronger in some ways and weaker in others.

C# and Java both stemmed from C/C++ languages, and they have a lot in common beyond both being Object-oriented. In addition to some structural similarities between Java’s JVM and C#’s .NET CLR, each advanced on its own path with their respective development teams focused on different visions of what the language should be.

We don’t want to get lost in the argument of which language is better than the other, we just want to outline some of the features that developers in C# are using that we don’t have available to us in Java.

Let’s get started.


LINQ (Language-Integrated Query) was introduced to C# in 2007 to help developers query data from various sources. With it, we can write queries without needing to take into consideration the appropriate syntax for the specific database being called on. A component of LINQ, the LINQ provider, converts the query to a format that’s readable by the underlying source. For example, if we need to query data from a SQL database, the LINQ to SQL provider will convert the LINQ query into T-SQL so that the database can understand it.

To perform a query operation in LINQ, first the database is obtained, then the query is created and finally it’s executed. In LINQ to Object queries, this can be as simple as a single line of code, rather than writing complicated iterations of nested for each loops.

For example, let’s look at this code for filtering 2-digit numbers from a list in C#.

First, without using LINQ:

And then using LINQ in query syntax:

And method syntax:

Both syntaxes here are correct, the only real difference is that the query syntax looks more like SQL and the method syntax uses lambda expressions (and thus, looks like something we might write in Java).

Bottom Line: Many of the features that LINQ relies on, such as lambdas, are useful in their own right and already have equivalents that were implemented in Java. So, while we can use streams and lambdas for querying data, LINQ streamlines the process and removes much of the verbosity that still exists in Java.

2. Struct

Structs in C# are used similarly to classes. In fact, a struct can even be considered to be a “lightweight class” itself as it can contain constructors, constants, methods and more. The biggest difference between a struct and a class is that structs are value-types while classes are reference-types.

The most significant benefit of writing a struct over creating a class is that it is easier to ensure value semantics when constructing a value-type than it is when constructing a reference-type. As stated in Microsoft’s documentation, “a variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to the data.” So, one of the benefits of using a struct over a class is that the only way to alter its value from other parts of the code is by explicitly passing it as a reference.

Developers at Microsoft recommend using a struct in place of a class only for types which are smaller than 16 bytes, are immutable, are short-lived and are not frequently boxed. Under these circumstances, using a struct may also be slightly more efficient than using a class because it will more likely be stored in the stack rather than in the heap.


Bottom Line: In many situations, writing a struct can appear to save time on memory allocation and deallocation and, thus, be more appealing. The truth is, though, that value-types are stored wherever they are owned. Regardless of the apparent benefits or drawbacks of using structs, we don’t have to worry about any of this when it comes to Java.

3. Async/Await

By calling async on a code part, or more specifically on a method, that method will be executed on a separate thread so as to not block the current thread. When the code reaches the await command, it will continue running. If at that point, the async code hasn’t finished, then the execution will return to its calling method.

This can help improve the overall responsiveness of your application and help to reduce performance bottlenecks. Using asynchronous programming is very important for applications when trying to access the web and for all UI-related activities. Compared to previous techniques of implementing asynchronous programming, the use of async/await preserves the logical structure of your code and the compiler does the heavy lifting that used to be required of the developer.


The output:

Bottom line: CompletableFutures undoubtedly brought us closer to having equivalent capabilities in asynchronous programming in C# and Java. Still, the complicated nature of using it makes it no match for the ease with which the async/await keywords can be implemented.

4. Lazy<T> Class

Whether working in C# or in Java, many of us have implemented lazy initialization (or instantiation) so that an object is not created until the first time that it will be used. One of the more common instances that lazy initialization is used for is when an application loads many objects upon launching but only requires a few of them initially. In this case, we want to instruct unnecessary objects to initialize only when needed to improve the performance of our application.

Bottom Line: Implementing lazy initialization recently became much easier in Java (as did many other things) when lambda expressions were introduced in Java 8. Still, in C# we can use the Lazy<T> wrapper class which provides the semantics of lazy initialization for any class library or user-specified type.

5. Some Keyword Equivalencies

Useful features in languages don’t have to be as big of an undertaking as implementing something like LINQ in C# or modules in Java. Here are some keywords that help C# developers that we don’t have in Java:

a. as

The as keyword in C# attempts to safe-cast an object to a type, and if it can’t it returns null. Java’s instanceof is almost comparable, but it is a boolean that returns true if the types match and false if they don’t.

b. Yield

Yield and return yield are used in C# to perform custom and stateful iterations without an explicit extra class and without the need to create a temporary collection. Our best options for implementing iterations in Java seem to be accessing an external library or using lambdas which were introduced in Java 8.

c. var

Var is an implicit type that is determined by the compiler and is functionally equivalent to writing an explicit type (i.e. int, string, etc.). Aside from saving a few extra keystrokes, var allows for anonymous types which are most typically used in LINQ queries. We’re expecting to see a “var” identifier implemented in the newly anticipated Java 10 which will “extend type inference to declarations of local variables with initializers.”

d. Checked

In C#, we use the checked keyword to explicitly enable overflow checking for integral-type expressions. If the resulting value of some expression is outside the range of the destination type, we can use checked to force the runtime to throw an OverflowException. This is helpful because while constant expressions have overflow checking at compile time by default, non-constant expressions do not.

Tooling Ecosystems

There are many more differences between Java and C#, of course, some of which are rooted in the differences in the Java and .NET frameworks. With these differences, come differences in compatibilities of helpful tools that provide production monitoring and error tracking.

One of those tools is OverOps, that shows developers the complete source code and variable state across the entire call stack for every error in production. It currently offers support for JVM based languages, and .NET framework compatibility is coming in the next few months. For more information and to join the waiting list for our .NET Beta, click here.

Final Thoughts

At the end of the day, most of the features we mentioned here give C# developers an advantage in terms of the length and clarity of their code rather than in enabling them to write code that can’t be written in Java. It’s also true that much of what made Java a more verbose language was addressed, at least partially, by the inclusion of lambda expressions in the most recent version update. Still, a lot of these features that we find in C# and not in Java simplify the syntax of common use cases beyond what lambdas offer.

Again, we don’t want to get involved in the never-ending argument over which language is better, we’re just here to point out some of the differences between the two. Did we miss any features that you would love to have in Java? Let us know in the comments!

Tali has a degree in theoretical mathematics and covers topics related to software monitoring challenges. In her free time, Tali enjoys drawing, practicing yoga and spending time with animals.
  • Richard Markle

    Aren’t LINQ and JPA pretty similar in concept?

    • Jorge Garay

      Spring Jpa currently gives some really nice features currently and Java 9 is almost here, then again as programmers we must keep in mind that every languages is a tool and you don’t want to use the wrong tool for a job, it would be like hammering a screw, you may get it in but you would have it sooner and better with a screwdriver.

      • c_world_poster

        Yup. With Spring Data JPA, i almost NEVER right a query any more. With Java 8, i can do anything i would need to in LINQ… and be readable. As for XML … who still does that. If i need to, i use Groovy XML Slurper.

    • edino

      Linq works with almost anything, SQL, XML, in memory collections …, and it’s based on language features such as lambda and expression trees.

  • Andre Dias

    Really good and I agree that there is no perfect language.
    Now, we’d kill to know what Java has that C# would kill to have it!
    It’s fair, isn’t it?

    • Tali Soroker

      Spoilers! We’re working on it now, stay tuned (:

    • c_world_poster

      Spring, Spring Boot Hibernate, most of what is in Apache.org, Netflix Libs and tools, real choice in IDEs, more than one application server … i could go on. Language differences are of little importance in 2017.

      • Chris Bordeman

        Well, Microsoft’s Entity Framework is much easier to use and faster than Hibernate. And Spring is no better than Unity, and there is a Spring.Net of course, in addition to a ton of other IoC containers.
        You can use VS, VS Code. and Ryder for C#, and Unity 3D of course is the most popular game tool, and it uses C#. Of course there are other options, but these are the big ones.
        As for application servers, the C#/Asp.net trend for many years is to use self-hosted executables, so different app servers are not necessary. However, there are several available, not just IIS.

        • c_world_poster

          I ‘ve used Hibernate and EF. Hibernate has way more features. My experience has been that EF is slow.

          Spring is much more than Unity. It is not just a IoC container.

          Spring.NET, sadly is dead. And it uses XML. 🙁 .
          I’ve used NHibernate and Spring.NET BTW. Quite a bit.

          • Chris Bordeman

            I use Hibernate right now, having used Unity much longer. I haven’t explored all its features, but Unity is designed around LINQ and Expression Trees, and ease of use matters a lot more to me. Also, since .Net 4.51 was released all the benchmarks I’ve seen have had EF easily beating Hibernate in performance, especially against MSSQL.

            Perhaps Spring.Net may be dead, I just know it exists and have never touched it, know no one who has. Buit of course there are a lot more .Net framework containers than that. What are these non-container features of Spring.Net that can’t be found in any .Net library?

    • HaakonKL

      I would start with enums that aren’t just wrappers over integers.

  • Gonzalo Ortiz Jaureguizar

    Structs will be implemented on Java as Value Types, which are being develop under the umbrella of the Project Valhalla at OpenJDK http://openjdk.java.net/projects/valhalla/.
    Sadly, they are not going to be ready for Java 9, so we will need to wait, at least, until Java 10

    • gacl

      Valhalla Value Types look way more sophisticated than C# structs. They better be given that they are coming ~20 years later. C# structs allow mutability which was a mistake. There are lots of gotchas where mutable C# structs can cause hard to spot problems. Also, the cited recommended limitations of 16 bytes and short lived objects are very limiting. Notice that even the .NET 4.0 library tuples don’t use C# structs which is a classic value type use case.

      • http://bartoszsypytkowski.com/ Bartosz Sypytkowski

        1. More than 90% of C# structs, you’ll going to deal with are immutable (eg. DateTime or TimeSpan) and it’s a common knowledge inside community to stick to that. You can also enforce field immutability (readonly keyword).

        2. There’s no limit for struct size, 16B is only guideline as structs are passed by value (so the content of the struct is copied)… which also can be mitigated by using ref keyword to avoid copying.

        3. Tuples are usually used internally (it’s an anti-pattern to expose tuples in public API). Moreover current versions of .NET already introduced value tuples, as well as value tasks – a common API for asynchronous Future-like types, designed to introduce no heap allocations for hot paths, where asynchronous value may be already completed.

        • gacl

          C# 7 tuples, pattern matching, and record types are all extremely useful features that should be on this list, and all Java programmers should want. Structs are simply less useful of any of those. And, of course, all three of those killer features have been in Scala since v1.0 for JVM programmers that want that: native tuples, pattern matching, record types.

  • gacl

    #1 just shows LINQ as simple functional collections with filter or where. Java 8 has this with the same level of conciseness.

    public static List filterTwoDigitNumbers(List l) {
    return l.stream().filter(i -> i > 9 && i < 100).collect(Collectors.toList());

    • Tali Soroker

      In terms of querying in-memory data, you’re absolutely right. With the introduction of streams in Java 8, LINQ wouldn’t necessarily add much to our abilities to, for example, filter data from objects in the heap. I think that one of the main benefits that we get from LINQ is the ability to query data from other data sources such as SQL databases or XML documents without needing to change our syntax.

      • gacl

        Yes. The OP strictly showcases in-memory collections and we agree that Java 8 collections have that. For SQL databases, LINQ offers nice features like compilation time syntax checking that you don’t usually get with SQL, but most devs will still prefer plain SQL. Additionally, last I checked LINQ only fully integrates with Microsoft SQL Server and not with popular options like Postgresql. And there are tons of similarly competent library options for concise XML querying/processing.

        • Isaac

          There are many LINQ providers on .NET, including a Postgresql one.

          • gacl

            AFAIK, it is a completely third party unofficial solution not supported by either Postgres or Microsoft, right?

          • Isaac

            Yep. Just like lots and lots of third-party packages that exist in both .NET and Java ecosystems.

        • Chris Bordeman

          ” last I checked LINQ only fully integrates with Microsoft SQL Server and not with popular options like Postgresql”
          Maybe years ago in the very beginning of LINQ, but for as long as I remember, most providers have full support. All the big names at least.

          Also, LINQ works for objects/classes, xml, sql, in theory any oddball API you can imagine if there’s a provider. And instead of executing them, you can save the Expression Tree, edit it, or transmit it for execution, deferred or elsewhere.

          • Antonio Ortiz Pola

            Sadly this is not 100% right, “most providers have full support”, it is not true, the official mysql provider sucks, there is a third party, pomelo, but the implementation is broken in net core 2.1, the repo owner already said that he does not have the time, so, it is at least two big DBs, Mysql and Maria that are broken in net core 2.1. I would love to see async await in java, or the high quality libraries that are in java in c#, but we can have both, Java worked with the backwards compatibility that give libraries the support and time to grow, the open source grown big in java first, thats why you do not see an Haddop implementation in C#, on the other hand, C# is younger and could learn from some java errors, as a language have more syntactic sugar, a real generics implementations and so on… but the inclusion of open source as something primordial is somehow new in c#, Net core is breaking a lot of things between implementations, linq is is missing some immutable facilities, docker is also somehow new (you can only debug in Vs and Vs Code) and so on…
            I would be careful with Kotlin, it seems to have many of the advantages of C# or Scala, with direct access to the library ecosystem of java.

      • Kostas Xaris

        There is the Speedment library for using stream syntax for databases.

      • aleksny78

        Linq also is connected to so called Expression Trees, there’s nothing like that or close in java.

    • Erik Eckhardt

      One thing that supercharges LINQ is that all generic collections in C# implement IEnumerable. Second, C# LINQ is implemented entirely with extension methods.

      So there are *many* areas of conciseness in C# LINQ that Java streams completely missing out on:

      1. You have to do `.stream()` in Java, but not C#, which is heck-all annoying to a C# developer getting used to Java.
      2. You can’t create your own extensions on streams without some crazy junk and extra converting code all over the place.
      3. Yeah, you can write your own collectors and spliterators and such, but then you have to implement entire classes with four methods having very complicated signatures. `yield return` and `yield break` combined with extension methods kick the pants off that. Go read Jon Skeet’s EduLinq series to observe how you could write your own custom LINQ-like library from scratch in a couple of days and then be smashing collections into submission in no time.
      4. All it takes to write a whole new custom ecosystem of collection objects in C# that all work perfectly with LINQ (or your custom LINQ-like library, flawlessly!) is to implement IEnumerable. Wow. There’s no equivalent in Java that is just going to work with so little code. Java is good for the WET folks (We Enjoy Typing).
      5. You can’t actually say that `.collect(Collectors.toList())` is actually as concise as `.ToList()`. Well, you can say it, but you’d be wrong.

    • luigimx

      Linq is not about collections, you can imolememt linq with basically any type.

  • gacl

    #2: “Developers at Microsoft recommend using a struct in place of a class only for types which are smaller than 16 bytes, are immutable, are short-lived and are not frequently boxed” That is a really narrow use case. The forthcoming Java Project Valhalla, probably will be in Java 10, is delivering much more flexible values types, that will eliminate those limitations. They will be fully usable and efficient with large data as well as small and with long lived objects as well as short lived. And they will require immutability rather than warn against the gotcha scenarios of mutability.

    • Phil Bolduc

      A reason Microsoft’s recommendation is that structs are passed by value as opposed by reference. You can use huge structs, but if you pass it to a function, the runtime has to make a copy of the structure. This could lead to poor performance.

      • gacl

        I agree with your explanation, but that doesn’t change the fact that limiting structs to 16 bytes is a very narrow use case.

        • Phil Bolduc

          There is no limitation in the C# language specification on the size of a struct. You can create a struct that is 1GB in size if you want. It is only a recommendation by Microsoft in their ‘Type Design Guidelines’.

  • gacl

    #3: Async/Await… Java has great non-blocking concurrency options. Notably, they have non-blocking concurrency frameworks like Java. Scala has exactly language features like Async/Await, but using a framework like Akka is still generally preferable.

    • Phil Bolduc

      async/await isn’t about concurrency algorithms like lock free data structures. One of the biggest benefits is preventing threads from being blocked on IO. A thread can do quiet a lot of work while waiting 2 ms for data from redi, database or a API.

      • gacl

        Did you even read what I wrote?

        • Phil Bolduc

          I did read it. However, I believe the article was talking about language features not 3rd party frameworks. Indeed you could add Akka to your project, but that could seem like a big dependency just to read/write asynchronously to a database or file. Akka is a great actor model framework. However, having to understand the actor model and how to use it correctly may be a stretch for some teams. From the Akka I/O documentation: “The I/O API is completely actor based, meaning that all operations are implemented with message passing instead of direct method calls.”

          • gacl

            Yes, Akka is a big dependency and not suitable everywhere. The standard JDK offers non-blocking async file + network APIs, so you can use those directly. The standard JDBC API for SQL databases is blocking, so that is probably a limitation. JVM SQL database libs like Slick provide non-blocking APIs, but I’m not sure the details behind that.

            Also, Scala has async/await type language features, and they aren’t widely used in the JVM world.

          • Phil Bolduc

            I never meant to imply you cannot do async IO in Java. It is just not as easy as using async/await. According to this (like I said I am NOT a Java developer) https://stackoverflow.com/questions/31092067/method-call-to-future-get-blocks-is-that-really-desirable

            Begin Quote:
            Future offers you method isDone() which is not blocking and returns true if computation has completed, false otherwise.

            Future.get() is used to retrieve the result of computation.

            You have a couple of options:

            * call isDone() and if the result is ready ask for it by invoking get(), notice how there is no blocking
            * block indefinitely with get()
            * block for specified timeout with get(long timeout, TimeUnit unit)

            I am sure you can use call backs or Akka or something else to make it cleaner. In my opinion, if a developer could do:

            T result = await getData(); // insert any new fancy keyword instead of ‘await’.

            instead of dealing with isDone(), callbacks? and timeouts.

            I suspect that you are expert Java developer and can write correct async (NIO) code in Java in your sleep. However, not everyone is a good as you and anything that can make it easier and less error prone would be welcome, regardless of the programming language.

            I will leave it up to the other readers to make up their minds. I do not think you and I will see eye-to-eye on this topic. Plus I am biased as a .NET developer.

    • kss

      Akka.NET – I’ve worked on it.. For almost every library of Java, there is a .NET equivalent..

      • gacl

        That’s mostly true. At the same time, But the data science world has largely moved to Python, the distributed server-side services world has largely moved to Go. I wouldn’t recommend Java, C#, or Akka for most new projects. This post is over a year old 🙂

        • http://www.usa.gov/ TCast [✓🅶🅾]

          LOL @ Go. 🤣

          • gacl

            Laugh at it… Go is arguably a better designed language than C#/Java that fixes their flaws. and is a better tool for many jobs, maybe even most jobs. Three big improvements on C#/Java: 1. Removing exceptions. Multiple return values baked into core language spec. 2. Removing OOP classes. 3. goroutines/channels is super simple to program/maintain and has the full performance of complicated non-blocking async concurrency in C# or Java.

          • http://www.usa.gov/ TCast [✓🅶🅾]

            Hi. Literally zero top 500 business line applications are written in Go. That means Go = 0 income/revenue for devs.

  • gacl

    All of these reasons are weak. Java should have `var` style type inference like C#/C++/Scala which is actively being added in JEP 286. Java should also have declaration side type variance with generics like C#/Scala which is also actively being added with JEP 300. The active JEPs are actually pretty comprehensive at nailing feature improvements that won’t break backwards compatibility. There are plenty of other improvements that would break backwards compatibility, but you can use Scala or some other language for that.

    • Tali Soroker

      Thank you for your comments. We see a lot of back and forth between the development of these two languages, as a feature is added to one we often see a similar one with additional benefits added to the other. There are many groundbreaking features coming in the next couple of releases of Java, and I’m sure we’ll see many exciting features introduced to C# as well. I’m also looking forward to taking a look at some of the features that Java has pioneered that C# doesn’t have yet in my next post on this subject.

      • Phil Bolduc

        As am I.

    • c_world_poster

      Use Lombok and get var and val

  • kenyee

    Sounds like you want to try Kotlin 😉

  • Andrea Laforgia

    I agree that features like async/await would be convenient, but I disagree that those would be something we’d kill to have 🙂 As you say, many of those C# features can be easily replicated with functional constructs in Java 8 (which is been around for quite a while now, with Java 9 on its way). And if you want to carry out an honest comparison, you have to compare C# with the latest Java versions. I disagree on the necessity to have structs – their presence is, imho, a distortion of object-orientation (an oo language should implement objects as active actors sending message to each other and not mere data-containers). I think the “as” operator is quite dangerous with its implicit conversion to null (null should be banned from any new language). We could continue debating but thanks for taking the effort to write this article 🙂

    • Tali Soroker

      Yes, that MAY have been a slight exaggeration… (: Regardless, thanks for reading and sharing your thoughts!

  • Jonáš Kulhánek

    Missing Expression Trees as it is a great c# feature

    • aleksny78

      Many people confuse it with linq and use it w/o knowing

  • Diego Visentin

    A very similar async-await for JVM has been developed by Electronic Arts: https://github.com/electronicarts/ea-async
    About LINQ for JVM, there are some projects like http://www.jinq.org and https://www.jooq.org

    • Valery Silaev

      Let me plug a link to my own library for Async/Await in Java: https://github.com/vsilaev/tascalate-async-await

      It’s about to be released shortly (need to add some documentation, the code itself is complete). Comparing to the EA implementation it has several benefits:
      1. A notion of the Scheduler (the counterpart of the SynchronizationContext in .NET) and SchedulerResolver (get an effective Scheduler implicitly or explicitly) — it’s crucial to control where asynchronous code will be resumed and moreover, it’s extremely important to have a suspension context being propagated to an resumption context automatically.
      2. An ability to cancel async method from the outside (in the my library it works even simpler and more natural than .NET CancellationToken)
      3. An ability to check for interruption in an asynchronous method – critical if you have non-blocking (CPU-bound) asynchronous tasks
      4. Asynchronous generators — not available in .NET at all, but exists in upcoming ECMAScipt 2018.

      Your feedback is welcome!

  • c_world_poster

    Linq from my experience usually makes code difficult to understand and is misused. Honestly, most of the things stated have very little value in most programming situations.

    In 2017, it is all about the frameworks and the JVM beats .NET hands down. FYI … I’ve done .NET since its beginning (and VB classic before that) and Java since 1999.

    • Chris Bordeman

      Based on what?

      • c_world_poster

        which part? The second … read what the industry is saying. Also based on there is nothing in .NET like Spring Boot or Lagom or Dropwizard or Wildfly Swarm

  • Angus McIntyre

    You introduction to async/await is very misleading I’m afraid. Neither async nor await specify a threading model. All they do is mark a method for division into task continuations. Await captures and posts onto the current synchronisation context if one exists.