Module mit transitiven Abhängigkeiten

Java 9 Bootcamp Logo

Im zweiten Teil dieser Serie habe ich gezeigt, wie man Abhängigkeiten zwischen Modulen setzt setzt. Jetzt geht es um Sonderformen dieser Abhängigkeiten.

Transitive Abhängigkeiten

Abhängigkeiten werden mit dem neuen Keyword "requires" bestimmt. Eine normale Abhängigkeit bedeutet, dass ein Modul auf die öffentlichen Packages des anderen Moduls zugreifen kann. Wir erweitern nun unser Beispiel von vorhin. Eine Begrüssung soll nun auch einen "Titel" haben. Dafür bauen wir die Klasse Message:

package de.eppleton.datamodel;

public class Message{

    private final String text;
    private final String title;
        public Message(String text, String title){
                this.text = text;
                this.title = title;
        }

    public String getText(){
        return text;
    }

    public String getTitle(){ 
        return title;
    }
}

Diese Klasse möchten wir auch in anderem Zusammenhang verwenden und legen deshalb ein neues Modul "de.eppleton.datamodel" mit folgender module-info an:

module de.eppleton.datamodel{
    exports de.eppleton.datamodel;
}

Unsere Greeting wird in Zukunft als Message von der Klasse GreetingComposer erzeugt. Auch diese legen wir in ein neues Modul und erstellen dafür eine module-info:

module de.eppleton.greetingcomposer{
        requires de.eppleton.datamodel;
        exports de.eppleton.greetingcomposer;
}

Das Modul greetingcomposer kann also Klassen aus datamodel lesen und erklärt die package de.eppleton.greetingcomposer als öffentliche Schnittstelle. Dann erstellen wir die Klasse:

package de.eppleton.greetingcomposer
import de.eppleton.datamodel.Message;

public class GreetingComposer{

    public static Message createGreeting(String who){
        return new Message("Hello "+who+"!", "Wellcome");
   }
}

Jetzt ändern wir die Klasse "Hello" so ab, dass sie den GreetingComposer verwendet:

package de.eppleton.hello;
import de.eppleton.greetings.Greeter;
import de.eppleton.greetingcomposer.GreetingComposer;

public class HelloWorld {
    public static void main(String[] args) {
        Greeter.greet(GreetingComposer.createGreeting("modular world").getText());
    }
}

Später können wir auch noch den Greeter so ändern, dass er eine "Message" verwenden kann. Für jetzt genügt das aber. Nun müssen wir im "hello"-Modul noch eine Abhängigkeit setzen, damit wir die Klasse GreetingComposer auch verwenden dürfen:

module de.eppleton.hello {
        requires de.eppleton.greetings;
        requires de.eppleton.greetingcomposer;
}

Wenn wir nun das Ganze zu kompilieren versuchen gibt es ein Problem:

>javac -d modules --module-source-path src $(find src -name "*.java")
src/de.eppleton.hello/de/eppleton/hello/HelloWorld.java:7: error: Message.getText() in package de.eppleton.datamodel is not accessible
        Greeter.greet(GreetingComposer.createGreeting("modular world").getText());
                                                                      ^
  (package de.eppleton.datamodel is declared in module de.eppleton.datamodel, but module de.eppleton.hello does not read it)
1 error

Wir können nun noch eine Abhängigkeit auf das "datamodel"-Modul setzen. Für unser Projekt wäre das kein Problem. Aufwändiger wird es, wenn ein Modul viele Abhängigkeiten mitbringt. Da wird die Pflege der module-info-Klasse schnell selbst zum Problem. Stattdessen machen wir aus der Abhängigkeit auf "datamodel" im Modul "messagecomposer" auf "transitive". Damit steht sie automatisch jedem Modul zur Verfügung, das eine Abhängigkeit auf "messagecomposer" hat:

module de.eppleton.greetingcomposer{
        requires transitive de.eppleton.datamodel;
        exports de.eppleton.greetingcomposer;
}

SPI

Nun kompiliert und startet das Projekt wieder ohne Probleme.

Besuchen Sie unsere Workshops zum Thema Java 9 um mehr über dieses und weitere neue Features von Java 9 zu erfahren: