Java vs C# – Part 2
Updated on 2018-11-09
Introduction
This is part two of a series of posts on Java and C#. You can read the first part, on structure, here. This time, we are going to talk about a lot of things that weren’t covered before, and leave some stuff for future posts!
Object and Collection Initialization
C# offers an interesting syntax for initializing instance properties at the same time a variable is declared, which can be combined with parameterized constructors:
MyClass c = new MyClass("constructor argument") { OneProperty = 1, AnotherProperty = "two" };
It also offers a syntax for defining the values of a collection upon declaration:
List<string> list = new List<string>{ "A", "B", "C" };
Dictionary<int, string> dictionary = new Dictionary<int, string>{ { 0, "A" }, { 1, "B" } };
Any class with an instance Add method can use this syntax, because it’s this method that the compiler calls behind the scene. Inside the { }, we need to pass as many elements as the Add method takes, and of the same type.
Casts
In Java, we only have one kind of cast between types:
Object o = ...;
String s = (String) o;
But in C#, we have two: the same as in Java, plus a “dynamic”, that returns null if the types are not compatible:
Object o = 1;
String s = o as String; //s is null
The as operator can only be used with reference types (classes and interfaces) and also nullable types (like int ?). It does not automatically use conversion operators (see below).
In C# there’s also something related, the null coalescing operator. Basically, it allows this syntax for initializing a variable to some value only if it is null:
Object o1 = null;
Object o2 = new MyClass();
Object o3 = o1 ?? o2; //short for: o1 == null ? o3 : o1
Methods
Undefined Number of Parameters
Both Java and C# support methods with an undefined number of parameters. The syntax is slightly different, in C# being:
public void Print(params String [] args)
{
}
An in Java:
public void print(String... args)
{
}
Default Parameter Values
C# allows setting default parameter values. The default values can only be:
- null;
- Literals (“string”, 1, false, etc);
- Enumerated values (MyEnum.Value);
- Constant fields (MyClass.SomeField).
public void SayHello(String to = "me")
{
}
A method can have any number of parameters with default values, but these parameters need to come at the end, after any other parameters without default values. Java does not offer default parameter values, we have to use method overloading for that.
Changing Parameter Order
C#, unlike Java, also allows passing parameters in any order, by name, which is very useful when we have methods with a lot of parameters, and we don’t want to remember their exact order:
public void DoSomething(int a, string b)
{
}
DoSomething(b: "a string value", a: 10);
Passing Parameters by Reference
C# has two ways to pass parameters:
-
By value, the default, which is a pointer for reference types - classes and interfaces - and the actual value for value types - enumerations, structures;
-
By reference: the actual address of the variable is passed, similar to ** or & in C++.
Passing parameter values by reference can be done in one of two ways:
-
Forcing the parameter to have a value set;
-
Not forcing the parameter to have a value set.
One example that forces assignment uses the out keyword:
public void Execute(out int result)
{
result = 0;
}
And if no assignment is required, we use ref instead:
public void Swap(ref int a, ref int b)
{
int c = a;
a = b;
b = c;
}
Both out and ref are functionally equivalent, but out enforces a compile-time constraint that the parameter has a value set. Passing values by reference is particularly useful in the case of structures, because, since they are not passed by pointer, they need to be copied byte by byte, which can take some time, and they might also be boxed if the method argument is of a reference type; passing structures by reference only sends the address of the local variable.
By comparison, Java always passes parameters by value, meaning, basic types pass their actual bytes and the others are passed as pointers to the original variable.
Extension Methods
C# offers the concept of extension methods. An extension method appears to be part of some type, as an instance method of that type, but it doesn’t break encapsulation, because it really isn’t part of it, and it doesn’t have access to the type’s internals.
Extension methods are defined as static methods in static classes with a this parameter of the target type:
namespace Extensions
{
public static class StringExtensions
{
public static String Revert(this String s)
{
//...
}
}
}
We call an extension method just as we call a regular method, provided that the class that defines it is in context, either by being in the same namespace as the calling method’s class, or by having its namespace imported.
using Extensions;//this is where the StringExtensions class lives
//...
String text = "abcd";
String reverted = text.Revert();
Java has virtual extension methods, also called default methods, which provide a default implementation of a method in an interface, which is then inherited (and can be overridden) by classes implementing the interface. This is the building block for the Stream API introduced recently. I talked about default methods on the first part of this series, but here’s an example.
public interface MyInterface
{
default void doSomething()
{
//do something
}
}
Default methods are always public, so there’s no need to use the public qualifier.
Static Interface Methods
Java also allows defining static methods, with implementation, in interfaces:
public interface MyInterface
{
static void doSomethingElse()
{
//does something else
}
}
Like default methods, static interface methods are always public.
C# doesn’t have any way to add statics or code to interfaces.
Synchronized Methods
Both languages allow a method, static or instance, to be declared as synchronized, meaning, it will lock the object on which it is being called (or the class, if it is a static method). The Java syntax is:
public synchronized void myMethod()
{
//I am synchronized
}
While the C# one use the MethodImplAttribute attribute:
[MethodImpl(MethodImplOptions.Synchronized)]
public void MyMethod()
{
//I am synchronized
}
The syntax for acquiring a lock on an object is identical, but Java uses the synchronized keyword:
synchronized (this.someLock)
{
//...
}
And C#, lock:
lock (this.someLock)
{
//...
}
Inline Methods
In C#, it is possible to instruct the compiler to try to inline certain methods, through the MethodImplAttribute attribute:
public class Something
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ShouldBeMadeInline()
{
}
}
The actual decision, however, is up to the compiler. Java does not allow this.
Operator Overloading
Overloadable Operators
In C#, most operators (arithmetic, comparison, bitwise) can be overloaded for a class, similarly to C++. This means that C# allows them to be redefined, so as to implement a more friendly syntax, or just change the default one:
public static bool operator == (MyClass c1, MyClass c2)
{
return c1.MyProperty == c2.MyProperty;
}
In this example, I am changing the default == operator, which just does a reference comparison, to a value comparison, where the actual contents of the class are compared. If we change ==, we also need to change !=:
public static bool operator != (MyClass c1, MyClass c2)
{
return !c1 == c2;
}
Some of the basic operators (+=, for example), cannot be defined explicitly, but can be defined from others (+):
public static MyClass operator + (MyClass c1, MyClass c2)
{
return new MyClass(c1.MyProperty + c2.MyProperty);
}
It is also possible to compare unrelated classes, but the declaring class must appear as one of the arguments:
public static bool operator == (MyClass c, String s)
{
return c.MyProperty == s;
}
The argument order defines if the operator is to be applied, for example, the previous code applies to:
MyClass c = new MyClass();
bool areEqual = c == "some string";
But not to:
MyClass c = new MyClass();
bool areEqual = "some string" == c;
We can, however, add two times the same overloadable operator with the arguments switched, so that they can be called interchangeably. The String class overrides the == operator, so as to always do comparisons by value.
The number of arguments is defined by the operator, there are unary and binary operators, and the types they return cannot be changed. For example, operator == always expects two arguments of any kind but has to return a bool.
Type Conversions
C# also features a special kind of operator: type conversion. Actually, there are two, for implicit and explicit conversions. Implicit conversions do not need an explicit cast:
public static implicit operator string (MyClass c)
{
return c.MyProperty; //MyProperty is of type string
}
MyClass c = new MyClass();
string s = c;
Whereas explicit ones do:
public static explicit operator int (MyClass c)
{
return int.Parse(c.Property); //MyProperty is of type string
}
MyClass c = new MyClass();
int s = (int) c;
All operators need to be public and static.
Attributes
Attributes are static metadata that can be added to types, packages, parameters, members, variables and assemblies (in C#). Some attributes have meaning to the compiler, and in C#, they can even be used as an Aspect-Oriented Programming (AOP) mechanism, because some system attributes are evaluated at runtime.
Java offers the following out of the box attributes (called annotations in Java):
-
@FunctionalInterface: declares an interface as a functional interface (an interface with a single method);
-
@Override: informs the compiler that the method to which it is applied is an override of a method with the same name and signature declared in a base class;
-
@Deprecated: marks the method as deprecated, which issues a compile-time warning;
-
@SafeVarargs: suppresses warnings related to the (mis)use of varargs (undefined number of parameters);
-
@SuppressWarnings: tells the compiler to suppress certain warnings, passed as parameters.
There are also meta-annotations, annotations that apply to other annotations:
- @Retention: how is the annotation kept after compilation (SOURCE: only at source level, stripped at compilation, CLASS: kept at compile time, but ignored by the Java VM, RUNTIME: kept after compilation and considered by the JVM);
- @Documented: when the annotation is applied, some elements should be documented with JavaDocs;
- @Target: the single target of an annotation (ANNOTATION_TYPE: other annotations, CONSTRUCTOR: constructors only, FIELD: fields, LOCAL_VARIABLE: local variables inside methods, METHOD: methods, PACKAGE: packages, PARAMETER: method parameters or TYPE: any elements of classes);
- @Inherited: the annotation can be inherited from the type’s base class;
- @Repeatable: the annotation can be applied several times.
As you can see, an annotation starts with a @ and must be defined as an interface using a special syntax:
@Target(value=CLASS)
public @interface MyAnnotation
{
String name() default "";
int number(); //required since a default was not supplied
}
Annotation methods can only return basic types, enumerations and arrays of them, and cannot take parameters.
The application of the MyAnnotation annotation can be:
@MyAnnotation(number = 10) //uses default value for name
public class MyClass
{
}
In C#, attributes are not totally different, but there are some differences:
- An attribute must inherit from the Attribute class, and, by convention, should have the Attribute suffix;
- When applied, we can leave out the Attribute suffix;
- Attribute’s members can only be of basic types, enumerations, or arrays of them;
- Required attribute properties should be passed in the attribute’s constructor.
There are lots of attributes included in the .NET, so it is not practical to cover them all. I leave just some examples:
-
AttributeUsageAttribute: specifies the attribute’s applicability, only valid in attribute classes;
-
ObfuscateAssemblyAttribute, ObfuscationAttribute: instructs obfuscation tools to obfuscate the whole assembly, or just some elements;
-
AssemblyTitleAttribute, AssemblyFileVersionAttribute, AssemblyCopyrightAttribute, AssemblyTrademarkAttribute, AssemblyProductAttribute, AssemblyDescriptionAttribute, AssemblyCompanyAttribute, AssemblyCultureAttribute, AssemblyVersionAttribute, etc: information about the assembly, some of which is made available to Windows Explorer;
-
GeneratedCodeAttribute: informs that the class was generated by some automated mechanism, such as an IDE designer;
-
MethodImplAttribute: specifies certain settings for a method (synchronized, inline, etc);
-
NeutralResourcesLanguageAttribute: the default culture of the assembly;
-
ObsoleteAttribute; marks an element as deprecated, causing a compile-time warning if it is used;
-
SerializableAttribute: marks a class as being serializable;
-
NonSerializedAttribute: states that a class’ field should not be serialized;
-
MTAThreadAttribute, STAThreadAttribute: indicates the COM threading model of the method (MTA or STA);
-
ConditionalAttribute: method will only be called if some compilation symbol is present (DEBUG, RELEASE, etc);
-
PrincipalPermissionAttribute: states that a method can only be called by callers that match some security requirements (certain user names, belonging to one of a set of roles, being authenticated);
-
CallerFilePathAttribute, CallerLineNumberAttribute, CallerMemberNameAttribute: caller information that is passed to method parameters automatically (will be covered in the next post).
By applying an AttributeUsageAttribute, we can specify:
-
AllowMultiple: if there can be many occurrences of this attribute, in the same element;
-
Inherited: if the attribute’s presence and values is inherited in descending classes;
-
ValidOn: the elements to which the attribute can be applied (All, Assembly, Class, Constructor, Delegate, Enum, Event, Field, GenericParameter, Interface, Method, Module, Parameter, Property, ReturnValue, Struct).
Here’s an example of a C# attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = false)]
public class MyAttribute : Attribute
{
public MyAttribute(String required)
{
this.Required = required;
}
public String Required { get; private set; }
public Int32 Optional { get; set; }
}
And its usage, notice that we leave out the Attribute suffix and the declaration syntax:
[My("name", Optional = 10)]
public struct MyStruct
{
//...
}
Exceptions
Throwable Types
In .NET, any type can be thrown as an exception, but C# limits this to instances of the Exception class, or one derived from it.
In Java, we can only throw classes that implement the Throwable interface. The most common example of one such class is Exception.
Checked Exceptions
In Java, methods in classes, enumerations and interfaces must declare any exceptions that they may throw, except those inheriting from RuntimeException; these are considered special, in that they can be thrown at unforeseen situations – a division by zero, a null pointer access, etc. The syntax is for declaring the “expectable” exceptions is:
public void myMethod() throws MyException
{
}
Calling any method that declares that it may throw an exception requires that the calling code be wrapped in a try…catch block, where all checked exception types must be explicitly handled, or, of course, a superclass of them (catch is polymorphic):
try
{
myMethod();
}
catch (MyException ex)
{
}
In both languages, more specific exception classes need to be catched first, that is, if you want to catch an Exception and a MyException that inherits from Exception, the catch block for MyException must be the first. C# allows you to omit either the class variable or even the class, if it doesn't matter:
try
{
//...
}
catch(MyException)
{
//no need to refer to the exception instance
throw; //no argument required
}
catch
{
//all other exception types
}
Rethrowing Exceptions
In Java as in C#, we can rethrow an exception caught in a catch clause:
try
{
//...
}
catch (Exception ex)
{
throw ex;
}
However, if we do it like this in C#, we lose the stack trace prior to the method issuing the throw. If we don’t want that, the alternative syntax is:
try
{
//...
}
catch (Exception ex)
{
throw;
}
Notice that if we don’t pass an argument to throw, it will rethrow the current exception – of course, this only works inside a catch clause.
Iterations
In C# and in Java we can iterate through the elements of a collection using either iterators or a specific syntax, which for C# is:
foreach (String item in list)
{
//...
}
And for Java:
for (String arg : args)
{
//...
}
In C#, any collection that has a method GetEnumerator returning an IEnumerator can benefit of this syntax, normally, all collections inheriting from IEnumerable (non-generic) or IEnumerable<T>. Java has a similar requirement, but the method needs to be called iterator and the required interface is Iterator<T>, which is implemented by most collections.
Returning Enumerables
In C#, if we want to return enumerations of values from a method, we normally prototype the method as returning IEnumerable<T>. If we do, we have a simplified syntax for returning individual values from it, which is the yield keyword:
public IEnumerable<String> GetPhrases()
{
var count = 0;
for (var s in listOfStrings)
{
if (++count == 0)
{
yield break; //break loop
}
yield return s; //return one value, but continue loop
}
}
yield can be used to both return a value from an enumeration or to terminate it. When one uses it, there cannot be an explicit return statement.
Lambda Functions
Lambda functions in C# are built upon delegates and extension methods. A lambda is just syntactic sugar for calling methods – which can be defined inline, as anonymous ones -, and are most useful in collections processing:
List<MyClass> untidyCollection = ...;
List<MyClass> sortedAndFilteredCollection = untidyCollection.Where(c => c.MyProperty > 100).OrderBy(c => c.MyProperty).ToList();
In this example, Where and OrderBy are lambda functions defined over the IEnumerable<T> interface, which is implemented by List<T>. The types of the lambda variables can be omitted. This would be the same as:
class MyClassComparer : IComparer<MyClass>
{
public Int32 Compare(MyClass c1, MyClass c2)
{
return c1.MyProperty.Compare(c2.MyProperty);
}
}
List<MyClass> FilterCollection(List<MyClass> source, Int32 value)
{
List<MyClass> target = new List<MyClass>();
foreach (MyClass c in source)
{
if (c.MyProperty > value)
{
target.Add(c);
}
}
return target;
}
List<MyClass> SortCollection(List<MyClass> source)
{
List<MyClass> target = new List<MyClass>(source);
target.Sort(new MyClassComparer());
return target;
}
List<MyClass> untidyCollection = ...;
List<MyClass> sortedAndFilteredCollection = SortCollection(FilterCollection(untidyCollection));
Uuuf… see the gain?
A lambda can have several parameters:
Func<MyClass, Int32, MyClass> multiplicationTransformation = (c, multiplier) => new MyClass(c.MyProperty * multiplier);
MyClass source = ...;
MyClass target = multiplicationTransformation(source, 10);
In Java, things are very different. First, a lambda function can only be used over a functional interface, that is, an interface implementation with a single method. A method call that takes as a single parameter a class that implements this functional interface can be made a lambda function:
interface Operation
{
default int operate(int a, int b);
}
class Calculator
{
int doOperation(Operation op, int a, int b)
{
return op.operate(a, b);
}
}
Calculator calc = new Calculator();
Operation sumOperation = (int x, int y) -> x + y;
calculator.doOperation(sumOperation, 1, 2);
Also comparison:
List<MyClass> unsortedCollection = ...;
List<MyClass> sortedCollection = Collections.sort(unsortedCollection, (MyClass c1, MyClass c2) -> c1.getMyProperty().compareTo(c2.getMyProperty()));
And finally, action listeners:
JButton button = ...;
button.addActionListener(evt -> System.out.println("Button clicked"));
Expression Trees
Related, but even more interesting than lambda expressions, are expression trees. These can be defined using the same syntax as lambda expressions:
List<MyClass> untidyCollection = ...;
IQueryable<MyClass> untidyQueryable = untidyCollection.AsQueryable();
IQueryable<MyClass> sortedAndFilteredQueryable = untidyQueryable.Where(x => x.MyProperty > 100).OrderBy(x => x.MyProperty);
But now sortedAndFilteredQueryable is an IQueryable<T>, a standard interface for wrapping expressions that return something. From it we can access the underlying Expression, the base class for all expression
Expression expression = sortedAndFilteredQueryable.Expression;
if (expression is MethodCallExpression)
{
MethodCallExpression methodCall = expression as MethodCallExpression;
Method method = methodCall.Method;
foreach (Expression arg in methodCall.Arguments)
{
//iterate through each argument
if (arg is ConstantExpression)
{
//constant
}
else if (arg is MemberExpression)
{
//property or field
}
}
}
This capability to analyze at runtime an expression is the basis for, for example, translating expression trees to SQL or OData calls – Entity Framework, LINQ to SQL, NHibernate, WCF Data Services, all use expression trees. Unlike lambdas, which do not retain the expression used, just the result, expression trees do not execute, but instead describe a C# expression.
Auto Closing Blocks
Both Java and C# offer an interesting construct for making sure resources are released when they are no longer needed.
In C#, these resources must implement IDisposable, and the syntax for the using block is as this:
using (IDisposable ctx = new DisposableContext())
{
//...
}
It allows multiple disposables in the same block:
using (var disp1 = new DisposableContext())
using (var disp2 = new DisposableContext())
{
//...
}
In Java, the required interface is AutoCloseable and the feature is called try with resources:
try (AutoCloseable disp1 = new Something())
{
//...
}
The syntax for having several auto-closing variables is also allowed:
try (AutoCloseable disp1 = new DisposableContext(), AutoCloseable disp2 = new DisposableContext())
{
//...
}
This basically is the same as (in both languages):
MyClass c = new MyClass();
try
{
//...
}
finally
{
if (c != null)
{
c.Dispose(); //C#
c.close(); //Java
}
}
Conclusion
Well, not exactly a conclusion! There will be more posts, keep dropping by and sending your feedback!
Again, I need to thank Roberto Cortez (@radcortez) for his review of the Java parts! Thanks, man!