Wednesday, May 9, 2007

To the Java refactoring maniacs

Every once in a while I have a virtual fight with some Java programmer over refactoring tools. They are not available for C++, Python, whatever. How can you even use an IDE without refactoring tools? Do you know how much productivity you gain? The Java language is the best language in the world; it was designed to be "refactorable". Bullshit!

Let's see. Java is known to be a poor language from expressiveness' point of view. Tools save it somehow, and whenever I try to tell someone there are better languages out there, IDEs are thrown into my face and the refactoring tools they provide are the ultimate arguments for Java.

So I set out to prove that the most simple refactoring tool one can think of is not totally automatable, even in a language as simple as Java. Let's wait no more. I clicked Eclipse in my Applications/Programming menu, went and grabbed a cup of tea while it was loading, came back just to see it's asking about the workspace (first time use), grabbed another cup of tea while waiting, and set out to test my theory.

I entered this code:


import java.lang.Class;
import java.lang.reflect.InvocationTargetException;

public class MainClass {
public static void poorName() {
System.out.println("This is a stupid method.");
}

public static void main(String[] argv) {
try {
MainClass.class.getMethod("poorName", new Class [] {}).invoke(null, new Object[]{});
}
catch (NoSuchMethodException e1) {
System.out.println("there is no method with name poorName.");
}
catch (IllegalAccessException e2) {
System.out.println("there is an illegal access.");
}
catch (InvocationTargetException e3) {
System.out.println("invocation target exception.");
}
}
}


I was initially aiming at fewer lines of code, but then I decided to handle all the exceptions. Simply put, this program looks for a method called poorName in MainClass, using introspection. It then tries to call that method. If the method is not found or whatever, an error message is printed.

As I run this program, I get the nice message:


This is a stupid method.


Then, because I don't like the name of the method, I set out to rename it. Of course, what better way of renaming a method than right-clicking on it's name, and selecting "Rename" from the "Refactor" menu?

Tadam, no error message, method was renamed. Resulting code?


import java.lang.Class;
import java.lang.reflect.InvocationTargetException;

public class MainClass {
public static void goodName() {
System.out.println("This is a stupid method.");
}

public static void main(String[] argv) {
try {
MainClass.class.getMethod("poorName", new Class [] {}).invoke(null, new Object[]{});
}
catch (NoSuchMethodException e1) {
System.out.println("there is no method with name poorName.");
}
catch (IllegalAccessException e2) {
System.out.println("there is an illegal access.");
}
catch (InvocationTargetException e3) {
System.out.println("invocation target exception.");
}
}
}


When I run this code, guess what? It changed behavior. It now prints:


there is no method with name poorName.


which is true, but sad. There you go: method rename is not computable (anyone care for the mathematical proof?), even in a language as simple as Java (well, the language is not as simple as it used to be, but still).

To all Java refactoring maniacs out there: the next time you want to tell me you're not looking at another language because it doesn't provide automated refactoring tools, please think hard about why that happens in the first place.

6 comments:

Anonymous said...

Wow. How nicely you have proved that Java is such a crappy, crappy language.
Or maybe you should have chosen the correct "refactoring" tool? Ctrl+F, for example.

Ricky Clarkson said...

Refactoring tools become more and more useful the larger your codebase gets, and the more dependencies there are between parts of the codebase.

Now look at that the other way - refactoring tools become less useful the more decoupled and modular your code gets (and if the dependencies between modules are minimised).

Verbose mandatory static typing does make writing decoupled modular code harder, which is probably why Java, more so than many other languages, really does need to be edited with an IDE that supports refactoring.

So the real thing to pose at a Java developer who refuses to look at a language that doesn't have refactoring tools is - why should your code be so repetitive that you even need refactoring tools?

Anonymous said...

There are refactoring tools available for other languages. I use Visual assist X on a daily basis for c code (It's a Visual Studio plug-in) If you work in VS you should do yourself a favour and check it out.

Anonymous said...

Insight into Nothing. Great name for a blog.

Salo said...

I think that the problem can be easily extended into more directions:
1. You mention Java, I'd go out and mention that you can have the problem with probably any language that suports reflection. And why is this? becase the function name is identified by means of a string - and most IDEs will behave poorly when looking into strings. [Bottom line - it's not a Java weakness, but a weakness of all languages that have reflection implemented "string"-wise]. I would argue that it might not be a language weakness but a poor way of the language structure to give an IDE a hand :)

2. The problem of working with strings in order to represent something that they are not supposed to and then to parse them and do something with them in runtime is the actual problem that I think is out there. Same goes true for DB connection strings, SQL query strings and so forth. When you code something into a string that will be parsed and !executed! in runtime you can not operate properly on that item in design time - the compiler/refactor tool/etc will have a hell of a time trying to figure out things.

stefan.ciobaca said...

@anonymous: The point was that refactoring is not computable, even in a language as simple as Java.

@ricky clarkson: when confronted with this question, most programmers will say that they need to use refactoring on a codebase that wasn't written by them ;-)

@salo: 1. of course, I'm not saying this is something wrong with Java, but I'm saying that the guys who claim they can't live without an IDE with refactoring tools are dead wrong (in fact, you're better off without the IDE, because it might wrongly refactor the source for you, as proven here).

As for identifying functions using strings, well, it's pretty much the only possibility for languages that make a strong distinction between runtime and compile-time.

But I don't think this is a language weakness. It allows you to create some really expressive code.

2. Doing DSLs is a major problem today, and with today's widely spread programming languages there are only two ways to do it:

- do your own string based DSLs, with the problems you mention
- wait for the language designer to include your feature in the language core (e.g. Linq).