Local classes in Java, both named and anonymous, can access local variables defined in the method defining the class, as long as the variable is defined with the modifier "final". This restriction appears arbitrary, at least initially. However, it is this restriction that prevents the semantics of local classes accessing local variables from becoming unusable.
First, recall that local classes are the same as regular classes from JVM's point of view (dollar signs '$' the compiler inserts into their names do not make these classes special). However, these classes are special from the compiler's point of view, because of their syntax and the scoping rules for the variables that they access.
The compiler performs a relatively straightforward transformation - it creates an instance variable for each local variable referenced from the methods of the class, prefixing the name with "var$". It also creates a constructor that takes one parameter for each referenced local variable, and initializes the additional instance variables with the values passed to the constructor. Finally, each access to a local variable is replaced with an access to its corresponding instance variable.
The same mechanism could have worked without requiring local variables to be declared "final": we simply need to ensure that the referenced local variable is initialized at the time the instance of the local class is created. Compiler already performs this check, so the use of "final" seems redundant. Unfortunately, relaxing this restriction would result in substantially less clear semantics, without giving much in return. Here is the code illustrating my point (obviously, it does not compile):
public static Object test() {
String x = "x";
class Inner {
public String toString() {
String tmp = x;
x = "y";
return tmp;
}
}
Object res = new Inner();
System.out.print(x);
System.out.print(inner.toString());
System.out.print(x);
return res;
}
The problem is that nearly any programmer reading this code would expect the code to print "xxy", not "xxx", as it, no doubt, would, had the program above compiled. The reason for that is that x="y" becomes var$x="y", leaving the local variable x intact. However, the code says otherwise, because there simply isn't a second variable!
I believe that this is precisely the problem that resulted in the additional restriction, as there is no other way to hide the mechanism based on copying values. Moreover, there is no way around copying values, because instances of local classes must be able to exist outside the scope of the method that declared their local classes. The easiest way to visualize this is to think about the behavior of the Inner class once the test() method has returned - x is no longer on the stack, so it must have a copy.
Wednesday, December 10, 2008
Monday, December 8, 2008
On Interface Design with Extension Methods
Extension methods that Microsoft added to C# 3.0 initially look like a syntax sugar. Initially, the idea appears very simple: Microsoft needed a way to add functionality to classes and interfaces that they cannot change; extension methods appear to be a quick and dirty way they dealt with the problem.
However, extension methods have a less intuitive use: they let designers provide implementations "horizontally," as opposed to providing it "vertically" through the chains of inheritance. Other languages such as C++ and Java do not have a comparable mechanism. In C++ you would end up placing functionality in a base class, and inheriting it virtually; in Java you would make the interface very large (see ResultSet for an example) because you cannot put implementations into an interface.
Consider an example. Let's say that we are modeling an interface for accessing a row of data that we read from a database (similar to Java's ResultSet interface). Here is how the interface could look:
public interface IFieldSchema {
string Name { get; }
int Index { get; }
}
public interface IRowSchema {
int FieldCount { get; }
IEnumerable Fields { get; }
IFieldSchema GetField(string name);
}
public interface IRow {
IRowSchema Schema { get; }
T GetField<T>(int index);
void SetField<T>(int index, T value);
T GetField<T>(string name);
void SetField<T>(string name, T value);
}
Take a look at the last two methods of IRow: on one hand, they seem unnecessary, because you can do the same thing by first obtaining an index from the schema, and then calling the corresponding GetField/SetField method; on the other hand, this will cause inconvenience to the users of the interface who prefer using names instead. Convenience drives the addition of both methods to the interface - this is precisely what designers of Java's ResultSet did. In doing so, however, the developer of the interface pays for the convenience of developers using the interface with the effort of the developers who implement his interface. What's even worse than that is that the method will have the same code in all implementations.
At this point, the designer of the interface may either take an abstract class route, or build a class that provides the needed implementation statically. Here is how the above example would look with an abstract class:
public abstract class AbstractRow : IRow {
public IRowSchema Schema { get; private set; }
public abstract T GetField<T>(int index);
public abstract void SetField<T>(int index, T value);
protected AbstractRow(IRowSchema schema) {
Schema = schema;
}
public virtual T GetField<T>(string name) {
return GetField<T>(Schema.GetField(name).Index);
}
public virtual void SetField<T>(string name, T value) {
SetField(Schema.GetField(name).Index, value);
}
}
This is better, because developers of classes inheriting IRow no longer need to code the "extra" methods. Unfortunately, this will force them to inherit AbstractRow, which may not be possible, considering the single-inheritance scheme of C#.
Extension methods provide an elegant way of solving this problem by moving the code of the "extra"methods into an extension method. Inheriting IRow becomes much easier, while nearly all functionality remains in place.
public interface IRow {
IRowSchema Schema { get; }
T GetField<T>(int index);
void SetField<T>(int index, T value);
T GetField<T>(string name);
void SetField<T>(string name, T value);
}
public static class RowExtension {
public static T GetField<T>(
this IRow row, string name
) {
return row.GetField<T>(
row.Schema.GetField(name).Index
);
}
public static void SetField<T>(
this IRow row, string name, T value
) {
row.SetField(
row.Schema.GetField(name).Index, value
);
}
}
The reason I say "almost" is that implementations of IRow can no longer override the code for accessing fields by name. In neraly all cases, however, it's a good riddance - consider, for example, building a new class implementing an interface that is similar to java.sql.ResultSet, with half of its methods already coded for you!
However, extension methods have a less intuitive use: they let designers provide implementations "horizontally," as opposed to providing it "vertically" through the chains of inheritance. Other languages such as C++ and Java do not have a comparable mechanism. In C++ you would end up placing functionality in a base class, and inheriting it virtually; in Java you would make the interface very large (see ResultSet for an example) because you cannot put implementations into an interface.
Consider an example. Let's say that we are modeling an interface for accessing a row of data that we read from a database (similar to Java's ResultSet interface). Here is how the interface could look:
public interface IFieldSchema {
string Name { get; }
int Index { get; }
}
public interface IRowSchema {
int FieldCount { get; }
IEnumerable
IFieldSchema GetField(string name);
}
public interface IRow {
IRowSchema Schema { get; }
T GetField<T>(int index);
void SetField<T>(int index, T value);
T GetField<T>(string name);
void SetField<T>(string name, T value);
}
Take a look at the last two methods of IRow: on one hand, they seem unnecessary, because you can do the same thing by first obtaining an index from the schema, and then calling the corresponding GetField/SetField method; on the other hand, this will cause inconvenience to the users of the interface who prefer using names instead. Convenience drives the addition of both methods to the interface - this is precisely what designers of Java's ResultSet did. In doing so, however, the developer of the interface pays for the convenience of developers using the interface with the effort of the developers who implement his interface. What's even worse than that is that the method will have the same code in all implementations.
At this point, the designer of the interface may either take an abstract class route, or build a class that provides the needed implementation statically. Here is how the above example would look with an abstract class:
public abstract class AbstractRow : IRow {
public IRowSchema Schema { get; private set; }
public abstract T GetField<T>(int index);
public abstract void SetField<T>(int index, T value);
protected AbstractRow(IRowSchema schema) {
Schema = schema;
}
public virtual T GetField<T>(string name) {
return GetField<T>(Schema.GetField(name).Index);
}
public virtual void SetField<T>(string name, T value) {
SetField(Schema.GetField(name).Index, value);
}
}
This is better, because developers of classes inheriting IRow no longer need to code the "extra" methods. Unfortunately, this will force them to inherit AbstractRow, which may not be possible, considering the single-inheritance scheme of C#.
Extension methods provide an elegant way of solving this problem by moving the code of the "extra"methods into an extension method. Inheriting IRow becomes much easier, while nearly all functionality remains in place.
public interface IRow {
IRowSchema Schema { get; }
T GetField<T>(int index);
void SetField<T>(int index, T value);
}
public static class RowExtension {
public static T GetField<T>(
this IRow row, string name
) {
return row.GetField<T>(
row.Schema.GetField(name).Index
);
}
public static void SetField<T>(
this IRow row, string name, T value
) {
row.SetField(
row.Schema.GetField(name).Index, value
);
}
}
The reason I say "almost" is that implementations of IRow can no longer override the code for accessing fields by name. In neraly all cases, however, it's a good riddance - consider, for example, building a new class implementing an interface that is similar to java.sql.ResultSet, with half of its methods already coded for you!
Subscribe to:
Posts (Atom)