Java 9開始推出了模組(Module)系統,讓Java的結構更為嚴謹,並且提升JVM載入類別的效率(就是我們常常import一大堆類別那邊)。
什麼叫做更為嚴謹呢?原本我們要取用其他類別時,只要在要取用的類別中import進被取用的類別就好了,剩下就是看修飾子看不看得到而已;而在module中,還要定義類別可以給那些其他模組取用:
上圖右側,當Square類別要import右邊的圈圈時就會失敗,因為右邊圈圈所屬的module沒有聲明要exports給Square所屬module,就會import不到。
除了要多定義要給誰用外,映射的機制也需要額外定義能被取用的範圍,原本映射的機制是可以無視封裝修飾子的:
package flour;
public class Wheat{}
package flour;
class Manufacturer{
public String name;
public Integer est;
}
package bread;
import flour.Wheat;
import flour.Manufacturer; // 這邊會報錯,因為修飾子的關係,bread看不到flour中的Manufacturer類別
public class Baguette{
Class class = Class.forName("flour.Manufacturer");
Field[] fields = class.getFields(); // 透過映射就可以拿到不該看到的Manufacturer類別的屬性
}
我們來看看module的語法:
module <this module name> {
requires <other module names>;
exports <packages of this module to other modules that require them>;
opens <packages of this module to other modules via reflection>;
uses <services provided by other modules>;
provides <services to other modules> with <service implementations>;
version <value>;
}
module com.some {
// requires java.base -> 這行會隱藏在所有的module中,代表引入Java SE的基礎classes。
requires java.logging;
requires transitive org.acme; // 任何引用com.some的module也會需要org.acme,所以宣告transitive時,引用com.some的module不用額外再引用org.acme。
requires static com.foo; // 宣告static代表這個module只會在compile time用到
}
package demos.a;
public class L {}
package demos.a;
class M {}
package demos.b;
public class N {}
package demos.c;
public class O {}
module demos{
exports demos.a;
exports demos.b to other;
}
module other{
requires demos;
}
// module other可以引用到class L, class N;class M為default所以只能在demos.a這個package中被看見,class O所在的package demos.c沒有被module exports,所以也沒辦法被其他module看見。
module foo{
requires demos;
}
// module foo可以引用到class L;class N只被exports給module other,所以這邊看不見class N。
open module demos {}
這表示所有其他的module都將可以取用該module的package,這就相當於以往的package運作規則了。
Service:
module service{
exports service.a;
}
package service.a;
public interface L{}
Provider:
module provider{
requires service;
provides service.a.L with provider.b.M;
}
package provider.b;
public class M implements service.a.L{}
有了以上定義後,當有類別要使用這個Service時,在module中聲明要取用哪個Service,並在程式碼中透過ServiceLoader.load(Service類別),並呼叫.findFirst()找到第一個有提供該Service的Provider實作,或透過.iterator()找到需要的Provider:
module application{
requires service;
uses service.a.L;
}
public class Main{
public static void main(String[] args){
ServiceLoader<L> s1 = ServiceLoader.load(L.class);
L l = s1.findFirst().get();
}
}