Things I miss from Java while programming in C#
One of the perks of being a freelance programmer is that I get to program in a lot of different languages, either because the client has dictated a certain language, has left the choice up to me, or limited me by what is supported by a host (Hi PHP!).
As fate would have it, I have had the good fortune to have extensive experience with both C# and Java. While many articles will list things a programmer misses from C# while coding in Java (properties, LINQ, reified generics, type inference, named and optional parameters, closures, continuations), this post intends to look at things a Java programmer might miss while coding in C#.
1. Method return type covariance
Covariant method return types are one of the lesser known Java features (see Section 8.4.5 in the JLS). Basically, Java allows you to narrow the return type of a method when overriding it in a sub-class. For example:
public class Foo {
}
public class SubFoo extends Foo {
}
public class Bar {
final Foo foo = new Foo();
public Foo getFoo() {
return foo;
}
}
public class SubBar extends Bar {
final SubFoo subFoo = new SubFoo();
@Override
public SubFoo getFoo() {
return subFoo;
}
}
Although situations where this is needed are seldom, it is a nice feature to have when you need it. An good example of its usage is in the Apache Wicket framework. All pages in Wicket typically derive from the WebPage class. Thus, when you want to access information from a custom Wicket session you need to do something like this:
public class UserInfoPage extends WebPage {
public UserInfoPage() {
FooSession session = (FooSession) getSession();
String userName = session.getUserName();
...
}
}
However, if you add a class above UserInfoPage
called FooPage
, you can use a covariant return type to eliminate the unsightly cast:
public class FooPage extends WebPage {
@Override
public FooSession getSession() {
return (FooSession) super.getSession();
}
}
public class UserInfoPage extends FooPage {
public UserInfoPage() {
FooSession session = getSession();
String userName = session.getUserName();
...
}
}
To me this is preferable to adding another method called getFooSession()
. Having two methods can be confusing to a future maintainer (do I want getFooSession()
or getSession()
? What's the difference?) and needlessly clutters your IDE's code completion.
There is no equivalent in C#, short of adding a method. Will C# ever support such a feature? It's hard to say, but probably not anytime soon. There has been a request for covariant method return types since 2004. Microsoft response then was:
We hear this request a lot. We'll consider it for the next release.
After seven years and three major C# releases, it looks like they're still considering it.
2. Class-like enums
This one is not a big surprise, as it is typically mentioned as one of the few things that Java has done better that C#. In C#, enums are basically glorified integral types. As such, they can't contain any extra data without using attributes. While attributes are nice, accessing them requires quite a bit of boilerplate.
public enum MotionType {
Stationary,
Walking,
Running,
[Description("Time Travelling")],
TimeTravelling
}
...
MotionType type = MotionType.TimeTravelling;
var description = type
.GetType()
.GetField(type.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast()
.First()
.Description;
Console.WriteLine(description);
A further restriction of C# enums if that they can't be used as a generic constraint. For example, the following code would not compile:
public class Monster where TMotionType : enum {
}
According to C# MVP Jon Skeet, this is not a restriction of the CLR, but is actually a restriction of the Microsoft C# compiler. The best you can do to work around this is to use the broader constraint where : struct
, or the Unconstrained Melody library.
Java enums, on the other hand, are specialized classes. Like C# enums, Java enums can be used with switch
statements and be converted to integral types. However, Java enums go far beyond these simple uses. Java enums can have instance fields, instance methods and even per-constant methods. Java generics can also be constrained to take only enums.
Here's how the MotionType
enum could be written in Java (notice how easy it is to get the description in this case):
public enum MotionType {
STATIONARY("Stationary"),
WALKING("Walking"),
RUNNING("Running"),
TIME_TRAVELLING("Time Travelling");
public final String description;
MotionType(String description) {
this.description = description;
}
}
...
MotionType type = MotionType.TIME_TRAVELLING;
System.out.println(type.description);
This is just scratching the surface of Java enum capabilities. I highly recommend reading Chapter 6 of Effective Java or the Oracle enum documentation.
3. Anonymous inner classes
This is another it's-useful-when-you-need-it feature of Java that C# mostly addresses with events and delegates. The most common situation for anonymous inner classes in Java is for listeners:
Widget widget = new Widget() {
public void onInteraction(WidgetState state) {
...
}
};
In C#, this is handled using events and delegates:
Widget widget = new Widget();
widget.Interaction += state => {
...
};
The C# approach is superior in most cases. There's far less clutter and it's a lot easier to add listener support to a class in C#. Furthermore, since it's a language feature, you can count on it being more or less the same everywhere. However, it's not without drawbacks. You'll notice in the previous Java example, we can guarantee that there is only one listener. Furthermore, if we declare onInteraction
as abstract, we can also ensure that all concrete implementations of Widget
have an onInteraction
handler. The event/delegate approach as written cannot make the same guarantees.
Since C# does not support anonymous inner classes, the best we can do to emulate them is something like this:
Widget widget = new Widget(state => {
...
});
...or, if we wanted to make it a little more clear, we could use named parameters:
Widget widget = new Widget(onInteraction: state => {
...
});
Locally scoped final/readonly variables
In the book Code Complete 2, Steve McConnell recommends that you use final keyword whenever possible. The justification for this being that less mutable code is easier to keep track of mentally and less likely to be accidentally modified. Although the practice of making everything final can be weird at first, you eventually become so used to using final that you start to feel a little bit exposed whenever a variable isn't marked final.
C# has a nearly equivalent keyword to final
: readonly
. However, for some reason, readonly is only usable on the field level. In the local scope, there is no direct equivalent to final in C#. The best you can do is use const
, but this only works with compile-time constants, so
const string foo = Foo.getString();
...won't work.
Conclusion
In this article we looked at four features of Java that lack direct C# equivalents: covariant method return types, class-like enums, anonymous inner classes and locally-scoped final/readonly keywords. C# is often thought of as a "better Java". However, as this article has shown, this is not true. Java may not have as many bells and whistles as its newer cousin, but it still offers some compelling features that may never find their way into C#.
45 comments
sealed
is actually not the equivalent offinal
in a local scope. Thefinal
keyword in Java means different things, depending on where it is used. As a class modifier (i.e.final class
) or as a method modifier (i.e.final void Foo()
) the C# equivalent issealed
. As a field modifier (i.e.private final int foo
), the C# equivalent isreadonly
. As a local variable modifier (i.e.final int foo
) there is no C# equivalent.const
is only suitable for compile-time constants. So while this is valid in Java: It will not work in C# with theconst
keyword: