|
|
||
Andreas Schaefer's BlogJ2SE Archives3 Years later my Bitching became Code: Guilder POC ReleasePosted by schaefa on March 07, 2008 at 10:24 PM | Permalink | Comments (1)For the impatient readers I just wanted to announce the release of the Guilder POC which can be found on its wiki: https://madplanet.com/trac/guilder which intends to be a replacement for Maven 2 taking the cool features of Maven 1 and 2 and also incorporating some of the cool Groovy stuff to give building projects its Groove back. For all others here is the a little bit longer story. In December 2004 I first complained about Maven 2 and I never really started to embrace Maven 2 and still think that Maven 1 is the better solution. But now Maven 1 is dead and Maven 2 took over. Eventually I had to use it in my line of work and discovered even more problems. Because I don't think that Jelly is a good scripting language and never started to be used outside of Maven 1 in a bigger way I was looking for another scripting language that I could use as its replacement. Eventually someone pointed me to Groovy and the Maven 2 Groovy plugin. Even though I liked the idea of Groovy the Maven 2 Groovy plugin did not work for me. I did not want to have a plugin that execute Groovy code but that could be written in Groovy. I also did not succeed in mixing Ant statements with script code as I could do in Jelly which took most of the appeal of Groovy away and so a lot of time passed by. Well, somehow I stumbled over Gant which is Ant in Groovy and I saw in an example what I was looking for. Using closures and a Builder class Groovy was able to provide code that would exactly do that. Here is a simple example:
As you can see there is Ant's optional jUnit task intermingled with the check if the classpathName is not null and then it will be set a class path ref for the jUnit task. Beside the fact that I can mix and match Ant with Script Code this code also looks nicer because I don't have to deal with the closing tags and with other XML oddities. So nearly two months ago I started to think about a project that uses Groovy able to replace Maven 2 with scripted plugins. Now you might think that why replacing Maven 2 if I just could add the ability to create and deploy Groovy based plugins. The answer is that there is more that I don't like in Maven 2 than just the compiled Plugins and I hope that eventually all of these shortcomings are handled when this project is released. Here is a list of such features:
Then it took me around 4 weeks to finally get a POC release worked out and finally last Tuesday I was able to announce the release of it on the LA-JUG meeting. After that I heard from many other build tools that are growing like wheat: Apache's Buildr, Gant (heard from it before), Gradle and Graven. After a short scan I don't think that Buildr is a good idea because the instructions are written in Ruby but that's maybe just me. Gant is good but its Ant in a Groovy coat and Graven is the same for Maven 2. Gradle seems to be the closest to what I want to do but I think that there are still major differences between the two when it comes to the idea of plugins and probably other areas. If everything works out as expected I should be able to release the Alpha version in a month or two which should have a solid core and a few full blown plugins. Have fun - Andy All equals() are not born equalPosted by schaefa on October 29, 2004 at 02:37 PM | Permalink | Comments (24)During a discussion with a colleague we started talking about the problems of equals() with inheritance. He mentioned Joshua Bloch's Effective Java book which covers this topic quit well but more or less said that this problem cannot be resolved. This was enough to get me hooked onto this problem and I came up with a solution quite fast. Afterwards I started to think about other solutions and their problems. Because all of them made sense I started to question myself if my solution was even necessary and until now I am still not sure if it does or if it is just a solution for theorists in the ivory tower. But even if it is just theory I think it is an interesting problem to talk about. The equals() method defined in java.lang.Object has some restrictions to its implementation. Among other things it needs to symmetrical meaning that target of the method can switch places with the argument and it must yield the same result (if A is equal to B then B must be equal to A). Also it must be transient meaning that if A is equal to B and B equal to C then also A must be equal to C. There are two common ways to implement an equals() method:
Before I start explaining my solution I want to come up with a use case to reason why equals() should work with inheritance. The use case is quite synthetic but it is the best model I could think of that is short and intuitive. In math you have the irrational numbers representing numbers like 1, ½, pi or e. An extension of it are the complex numbers that contain two irrational numbers one for the real part and for the imaginary part. For more information visit Dave's short course on Complex Numbers. In complex numbers you can also express pure irrational numbers by setting the imaginary part to 0. This means that e = e + 0i. For us this means that an irrational number should be equal to a complex number if the imaginary part is set to 0. The algebra behind the complex numbers does ensure that equals is symmetrical and transient. Now, shouldn't this be good enough for us software developers to achieve the same? The first solution mentioned above fails because it breaks with inheritance and the second solution will break mathematical because any complex number with the same real part as the irrational number will be equal because there is no test against the complex part. To solve the equals() test with inheritance the equals() method must be written with inheritance in mind because only a sub class is able to determine equality correctly but in order to support symmetry the base class must delegate the test to the sub class if a sub class instance is given. An equals method of the irrational number would look like:
1 public boolean equals(Object o) {
2 if(o == null) return false;
3 if(o == this) return true;
4 if(o instanceof Irrational) {
5 if(o.getClass().equals(Irrational.class)) {
6 return ((Irrational)o).real == real;
7 } else {
8 return o.equals(this);
9 }
10 }
11 return false;
12 }
In line 5 we check if the given object is of the same type and then we test is like normal. In line 8 the given argument is of a sub class so we delegate the test to this sub class with this instance as parameter.
In the Complex class we need to adjust the equals method a little bit:\
1 public boolean equals(Object o) {
2 if(o == null) return false;
3 if(o == this) return true;
4 if(o instanceof Complex) {
5 if(o.getClass().equals(Complex)) {
6 return ((Complex)o).real == real && ((Complex)o).imag == imag;
7 } else {
8 return o.equals(this);
9 }
10 } else {
11 if(o.getClass().equals(Irrational.class)) {
12 return imag == 0 && ((Irrational)o).real == real;
13 }
14 }
15 return false;
16 }
The only difference to the Irrational's equals method is that we here check in line 11 if the given object is of type Irrational and then we allow the instances to be equal if the imaginary part is set to 0 and the real part is equal. So line 8 in both classes ensures that the equals() method is symmetrical and line 11 to 13 in the Complex class ensures that it is transient. Even thought this looks great so far it has three problems:
The solution above works quite well even when it has some restrictions. Still, you might think that this is not useful in the real world but imagine a scenario where you have an ArrayList of Irrational type (generic types in JDK 1.5). This allows you to add Irrational numbers but also Complex numbers. If the equals() method would not support inheritance you would not be able to find an Irrational instance with a Complex instance that represents the same irrational number because the imaginary part is set to zero. One would also fail if one will search a Complex number with imaginary part set to zero with an Irrational instance. In a world of distributed computing sometimes we cannot just change all the code and so inheritance provides a way to introduce changes with localized side effects. This also means we must put some effort in the design of a class so that inheritance does not break especially on crucial parts like equality. Happy coding – Andy Proposal to fix the Cloneable ProblemPosted by schaefa on October 13, 2004 at 09:02 AM | Permalink | Comments (9)I know that probably backward compatibility is the main reason to keep java.lang.Cloneable as it is. Nevertheless as I hopefully showed in my rant about this still unresolved issue this shortcoming of the Cloneable interface is still haunting us. To prevent the impression that I only complain about problems I am going to suggest a solution to this problem that, I hope, will end this problem once and for all. I would suggest the following addition to the JDK (Please adjust the name of the interface as you like I just could not think of a better one):
package java.lang;
public interface XCloneable
extends Cloneable {
// The exception still can be thrown if a reference is not cloneable
public Object clone()
throw CloneNotSupportedException;
}
Now all the classes in the JDK that implements Cloneable and provide a public clone() method could now extend XCloneable and we all could life happily ever after.
This should fix the shortcoming of Cloneable and still is backward compatible. It is just ugly to add another interface but, I guess, that is the price for backward compatibility. Let's go Tiger - Andy | ||
|
|