Inselupdate: Vererbung und Überschattung von statischen Variablen

Die Konstanten einer Schnittstelle können einer anderen Schnittstelle vererbt werden. Dabei gibt es einige kleine Einschränkungen. Wir wollen an einem Beispiel sehen, wie sich die Vererbung auswirkt, wenn gleiche Bezeichner in den Unterschnittstellen erneut verwendet werden. Als Basis unseres Beispiels steht eine Schnittstelle BaseColors mit ein paar Deklarationen von Farben. Zwei Unterschnittstellen erweiterten BaseColor, einmal CarColors und PlaneColors, die für Farbdeklarationen für Autos und Flugzeuge stehen. Eine besondere Schnittstelle FlyingCarColors erweitert die beiden Schnittstelen CarColors und PlaneColors, denn es gibt auch fliegende Autos, die eine Farbe haben können.

interface BaseColors
{
  int WHITE   = 0;
  int BLACK   = 1;
  int GREY    = 2;
}

interface CarColors extends BaseColors
{
  int WHITE   = 1;
  int BLACK   = 0;
}

interface PlaneColors extends BaseColors
{
  int WHITE   = 0;
  int GREY    = 2;
}

interface FlyingCarColors extends CarColors, PlaneColors
{
}

public class Colors
{
  public static void main( String[] args )
  {
    System.out.println( BaseColors.GREY );      // 2
    System.out.println( CarColors.GREY );       // 2
    System.out.println( BaseColors.BLACK );     // 1
    System.out.println( CarColors.BLACK );      // 0
    System.out.println( PlaneColors.BLACK );    // 1

    System.out.println( FlyingCarColors.WHITE );//  The field FlyingCarColors.WHITE is ambiguous
    System.out.println( FlyingCarColors.GREY ); //  The field FlyingCarColors.GREY is ambiguous
  }
}

Die erste wichtige Tatsache ist, dass unsere drei Schnittstellen ohne Fehler übersetzt werden können, aber nicht die Klasse Colors. Das Programm und der Compiler zeigen folgendes Verhalten:

  • Schnittstellen vererben ihre Eigenschaften an die Unterschnittstellen. CarColors und auch PlaneColors erbten die Farbe WHITE, BLACK und GREY aus BaseColors.
  • Konstanten dürfen überschattet werden. CarColors vertauscht die Farbdeklarationen von WHITE und BLACK und gibt ihnen neue Werte. Wird jetzt der Wert CarColors.BLACK verlangt, liefert die Umgebung den Wert 0, während CarColors.BLACK 1 ergibt. Auch PlaneColor überdeckt die Konstanten WHITE und GREY obwohl sie Farbe mit dem gleichen Wert belegt sind.
  • Erbt eine Schnittstelle von mehreren Oberschnittstellen, so ist es zulässig, wenn die Oberschnittstellen jeweils ein gleichlautendes Attribut haben. So erbt etwa FlyingCarColors von CarColors und PlaneColors den Eintrag WHITE, BLACK und GREY.
  • Unterschnittstellen können aus zwei Oberschnittstellen die Attribute gleichen Namens übernehmen, auch wenn sie einen unterschiedlichen Wert haben. Das testet der Compiler nicht. FlyingCarColors bekommt aus CarColors ein WHITE mit 1 aber aus PlaneColors das Weiß mit 0. Daher ist auch der Zugriff FlyingCarColors.WHITE in dem Beispiel Colors auch nicht möglich und führt zu einem Compilerfehler. Bei der Benutzung muss ein unmissverständlicher qualifizierter Name verwendet werden, der deutlich macht, welches Attribut gemeint ist, also zum Beispiel CarColors.WHITE oder PlaneColors.WHITE. Ähnliches gilt für die Farbe GREY. Obwohl Grau durch die ursprüngliche Deklaration bei BaseColor und auch bei der Überschattung in PlaneColors immer Zwei ist, ist die Nutzung durch FlyingCarColors.GREY nicht zulässig. Das ist ein guter Schutz gegen Fehler, denn wenn der Compiler dies durchließe, könnte sich im Nachhinein die Belegung von GREY in BaseColors oder PlaneColors ohne Neuübersetzung aller Klassen ändern, und zu Schwierigkeiten führen. Diesen Fehler – die Oberschnittstellen haben für eine Konstante unterschiedliche Werte – müsste die Laufzeitumgebung erkennen. Doch das ist nicht möglich und in der Regel setzt der Compiler die Werte auch direkt in die Aufrufstelle ein und ein Zugriff auf die Konstantenwerte der Schnittstelle findet nicht mehr statt.

Google Guava: Delete all files in a directory (now deprecated)

The java.io.File class can delete a file but not a collection of files or a whole directory including its containing files. com.google.common.io.Files declares two methods to do this job:

  • static void deleteDirectoryContents(File directory)
    Delete all the files within a directory but not the directory itself. If the contents of this or any subdirectory can’t be listed an IOException("Error listing files for " + directory) will be thrown.
  • static void deleteRecursively(File file) throws IOException
    Delete a file or directory and all contents recursively. This methods calls deleteDirectoryContents() first and then file.delete(). If this directory can’t be deleted an IOException("Failed to delete " + file) will be thrown.

Because a file can be a symbolic link it is important to understand the semantics of these methods. If a containing file or directory is a symbolic link will the symbolic link itself be deleted or will the method follow the link and delete the target? None of the methods follow symbolic links because it’s simply not possible with the JDK. If these methods find a symbolic link then just the link will be deleted. But what if the methods are called on a directory which is itself a symbolic link?

If deleteDirectoryContents() is called on a File object which is a symbolic link to a directory nothing will happen at all because as already mentioned symbolic links are not followed; so deleteDirectoryContents() will not delete the directory the method was called on. If on the other site deleteRecursively() is called on a symbolic link then just the symbolic link to this directory is deleted but not the target directory itself. That’s just how symbolic links works in Java and has nothing to do with the actual deleteXXX()-methods.

 

Alternative: Use NIO.2 from Java 7 or copy the old source code to your project.

Google Guava 11

Neues unter http://code.google.com/p/guava-libraries/wiki/Release11, genauer http://docs.guava-libraries.googlecode.com/git-history/v11.0/jdiff/changes.html. 2 neue Pakete sind:

Mit der neuen MathInt Klasse beschäftigt sich ausführlicher der Blogpost http://marxsoftware.blogspot.com/2011/12/guava-release-11s-intmath.html.

Guava AppendableWriter (internal class) and CharStream.asWriter()

With the current version the class is now package visible! If you want to use the class, copy it in your project and make it public. Or make use of CharStream.asWriter().

 

The classes StringWriter, CharArrayWriter and ByteArrayOutputStream have two things in common: a) They are sinks and when you write into them you ask these classes for the collected result; b) the internal buffer always starts empty. So for getting the result the classes offer different methods:

  • StringWriter: toString() return the buffer’s current value as a string.
  • CharArrayWriter: toCharArray() returns a copy of the input data as a char array; toString() returns the input data as a String.
  • ByteArrayOutputStream: toByteArray() returns a newly allocated byte array with a copy of the stream data. toString(String enc) converts the buffer’s contents into a String object, translating bytes into characters according to the given character encoding. The parameter less methode toString() uses the default encoding.

One can see the second point, that every of these sink classes starts with an empty buffer, as a disadvantage. If one wants to append to an existing String or char or byte array this has to be done in a second step. It would be nice to have a class, lets say StringBuilderWriter, which writes into a mutable StringBuilder. But Java SE doesn’t offer such a class.

The Google Collection library provides a class AppendableWriter which writes into an Appendable. The Appendable interface was introduced in Java 5 and is implemented by classes to whom you can append chars or Strings to. It dictates three methods:

  • Appendable append(char c)
  • Appendable append(CharSequence csq)
  • Appendable append(CharSequence csq, int start, int end)

Implementing classes are among others:

  • StringBuilder, StringBuffer
  • Every Writer-class; the base class implements Appendable (this was retrofitted in Java 5)

So to write into a StringBuilder all you have to do is:

StringBuilder sb = new StringBuilder( "start-" );
Writer w = new AppendableWriter( sb );
w.write( "middle" );
w.close();
sb.append( "-end" );
System.out.println( sb ); // start-middle-end

So with AppendableWriter its easy to represent every Appendable as a Writer. This is exactly what the static method asWriter() in the utility class CharStreams does:

public static Writer asWriter(Appendable target) {
 if (target instanceof Writer) {
  return (Writer) target;
 }
 return new AppendableWriter(target);
}

If the Appendable is already of subtype Writer it does not make sense to wrap it in an AppendableWriter again, so the target is directly returned. But what happens if you call close() or flush() on this special Writer—an Appendable doesn’t have close() neither flush()? The answer is simple: If you call close()/flush() on an AppendableWriter the implementation checks if the constructor argument implements Closeable/Flushable and calls close()/flush() accordingly. That means if you close/flush this Writer the close()/flush() operation will be delegated otherwise—for example in the case of StringBuilder with does neither implement Closeable nor Flushable—nothing happens.

FindBugs 2 ist da

Nach dem auf der Mailingliste schon seit längerem eine Version 2 angekündigt wurde, mussten Entwickler doch noch längere Zeit warten. Jetzt ist es soweit. Infos unter http://findbugs.sourceforge.net/findbugs2.html und http://code.google.com/p/findbugs/w/list.

Die Version 2 kann man gleich über JNLP mit ein paar Sourcen ausprobieren:

Im JDK finden sich immer noch einige Fehler, toll, was FindBugs so findet. Das hier zum Beispiel:

  • if (name != null || name.length > 0).  Ist name == null, wird name.length zu einer NPE führen. Hier muss ein && statt ||.

oder

  • if (lookbehind.length() == 1) {
      char c0 = (lookbehind == null) ? ‚|‘ : lookbehind.charAt(0);

oder

  • if (!wellformed){
      if (!wellformed){

oder

if ((parent instanceof LiteralElement) ||
        (parent instanceof LiteralElement)) {

oder

  • boolean isInitValueValid(int v) {
       if ((v < 0) || (v > SnmpUnsignedInt.MAX_VALUE)) {

COOL!

Erster Draft von JSR-335 (Lambda) verfügbar + Devoxx Folien

Siehe http://mail.openjdk.java.net/pipermail/lambda-dev/2011-November/004191.html bzw. http://jcp.org/en/jsr/detail?id=335.

Folien zur aktuellen Lambda-Version von der Konferenz sind hier: http://blogs.oracle.com/briangoetz/resource/devoxx-lang-lib-vm-co-evol.pdf.

Der aktuelle Compiler weist aber noch Unterschiede auf, sie werden hier diskutiert: http://mail.openjdk.java.net/pipermail/lambda-dev/2011-November/004253.html.

Ergebnisse “JDK8 Warnings Cleanup Day”

In den Dokus bzw. im Code sind immer wieder kleine Bugs und Warnings, die leicht behoben werden können. Daher lief am 1. Dez. ein Cleanup Day: http://mail.openjdk.java.net/pipermail/jdk8-dev/2011-December/000380.html, http://wikis.sun.com/display/OpenJDK/JDK8+Warnings+Cleanup+Day. In der Mailingliste werden die Bugs besprochen und es ist ganz interessant, was da rausgekommen ist.

Adventszeit, Zeit zum Nachdenken über Scala …

… und das macht Stephen Colebourne in einem Blog in einem kritischen Beitrag http://blog.joda.org/2011/11/scala-feels-like-ejb-2-and-other.html. Das produziert eine Menge an Antworten, durchaus lesenswert, auch die folgenden Blog-Einträge http://blog.joda.org/2011/11/scala-ejb-2-feedback.html und http://blog.joda.org/2011/11/real-life-scala-feedback-from-yammer.html. Und obwohl ich Programmiersprachen immer ganz reizend finde, macht mich Scala auch nicht so scharf, und ich war nie motiviert sie die lernen.