Java Blend

JVM-Sprachen im Mix

Eckard Buchner

Scala wird auf den JSR- und BSF-Seiten nicht als unterstützte Sprache aufgeführt. Man kann aber die Interpreter-Klasse aus dem scala-compiler.jar-Archiv direkt aufrufen:

Interpreter interpreter = new Interpreter(new Settings());
interpreter.interpret("println("Hallo Scala")");

Eine standardkonforme Integration wäre dem natürlich vorzuziehen. Auch für Skripte gilt demnach, dass Groovy leichter integrierbar ist.

Gemischte Klassenhierarchien

Groovy-, Scala- und Java-Klassen kann man im Prinzip beliebig voneinander ableiten, wenn man die Regeln der Joint Compilation beachtet. Auch Schnittstellen lassen sich in anderen Sprachen implementieren. Die aktuellen Groovy-Versionen ignorieren allerdings die Sichtbarkeitsregeln für Methoden und Felder [19] und erlauben den Zugriff auf private und geschützte Attribute und Methoden. Wenn Sie umgekehrt in Groovy etwas als private oder protected deklarieren, honorieren das andere Sprachen sehr wohl. Denken Sie bei Methodendeklarationen mit dem Typ def auch daran, dass der Compiler daraus immer einen Rückgabewert vom Typ Object ableitet, und zwar auch dann, wenn Sie keinen Wert explizit zurückgeben.

def testMyClass() {...}
static def main(args) {...}

JUnit wird die erste Test-Methode ignorieren. Der Java-Interpreter findet im zweiten Fall die main()-Methode nicht. Ersetzen Sie in diesen Fällen einfach def durch void. Es empfiehlt sich eigentlich fast immer, den Typ des Rückgabewerts ausdrücklich anzugeben. Sie verbessern damit die Lesbarkeit des Codes.

Scala Traits sind am ehesten mit abstrakten Klassen zu vergleichen, wobei eine Scala-Klasse sich von mehreren solchen Traits ableiten kann. Zunächst ein einfaches Beispiel einer Scala-Klasse, die zwei Traits einbindet (Listing 5).

Listing 5

trait Logging {
  def info(msg: String) {
    println("Info: " + msg);
  }

  def error(msg: String) {
    println("Fehler: " + msg);
  }
}
(Logging.scala)

trait NumberFormat {
  def format(number: Int) = number.toString
}
(NumberFormat.scala)

class SCalculator extends Logging with NumberFormat {
  def multiply(op1: Int, op2: Int): Int = {
    info("Berechne " + format(op1) + " * " + format(op2))
    op1 * op2
  }
} 
(SCalculator.scala)

Versucht man in Groovy oder Java von den Logmechanismen oder den Formatieralgorithmen zu profitieren, scheitert man schnell. Die Compiler verweigern den Versuch, sich von einem Trait abzuleiten oder einen solchen zu instanziieren. Der Scala-Compiler erzeugt aus dem Logging-Trait zwei Class-Files: Ein gleichnamiges Interface Logging und eine abstrakte Klasse Logging

psenv::pushli(); eval($_oclass[„class“]); psenv::popli(); ?>

, in der die Implementierung in statischen Methoden steckt. Technisch ist eine Einbindung also dadurch möglich, dass man das Logging-Interface mithilfe der statischen Methoden der Logging

psenv::pushli(); eval($_oclass[„class“]); psenv::popli(); ?>

-Klasse implementiert (Listing 6).

Listing 6

public class JLoggingImpl implements Logging {
    public void info(String msg) {
        Logging$class.info(this, msg);
    }
    ...
    // required but undocumented in Scala API
    public int $tag() {
        return 0; 
    }
}
(JLoggingImpl.java)

Dafür müssen wir leider auch noch die

psenv::pushli();$env->object_param[0]=““; eval($_oclass[„tag“]); psenv::popli(); ?>

-Methode aus ScalaObject implementieren (siehe Diskussion in [20]). Und was passiert, wenn der Logging Trait z. B. um warning()– oder debug()-Methoden erweitert wird? Sie müssen dann für jede neue Methode Ihren Java- oder Groovy-Code ändern, auch wenn die Methode nicht abstrakt ist. Vielleicht ist es geschickter, eine abstrakte Hilfsklasse in Scala zu schreiben, die die gewünschten Traits einbindet, und sich dann davon abzuleiten.

Groovy bietet zwei verschiedene Mechanismen für die Erweiterung von Objekten und Klassen: Metaprogrammierung zur Compilezeit mit AST-Transformationen und Metaprogrammierung zur Laufzeit. So hinzugefügte Methoden können im Bytecode erzeugt werden, wie bei @Delegate oder @Bindable, und sind dann auch von Java und Scala aus sichtbar. Methoden, die dynamisch zur Laufzeit hinzugefügt werden, sind nur für Aufrufe durch das Groovy Meta-Object-Protocol sichtbar.

Geschrieben von
Eckard Buchner
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.