2017-05-29 54 views
10

假如我這樣做是jshell:有沒有辦法在jshell中使用頂級函數的方法引用?

jshell> void printIsEven(int i) { 
    ...>  System.out.println(i % 2 == 0); 
    ...> } 
| created method printIsEven(int) 

jshell> List<Integer> l = Arrays.asList(7,5,4,8,5,9); 
l ==> [7, 5, 4, 8, 5, 9] 

jshell> l.forEach(/* ??? */); // is it possible to use a method reference here? 

在一個正常的程序,我可以在一個名爲MyClass類的靜態背景下,非靜態上下文或l.forEach(MyClass::printIsEven)l.forEach(this::printIsEven)

在jshell使用this::printIsEven不起作用,因爲jshell在靜態上下文中執行語句,但因爲沒有類名的前綴::printIsEven,並試圖l.forEach(::printIsEven)只是一個語法錯誤,你不能用靜態方法的參考。

+0

我沒有用過JShell,但你不能讓方法靜態? –

+1

@ChandlerBing不,產生'頂級聲明中不允許使用'Modifier'static',忽略' –

回答

13

您可以爲創建一個新的類:

jshell> class Foo { static void printIsEven(int i) { 
    ...>  System.out.println(i % 2 == 0); 
    ...> }} 
| created class Foo 

jshell> Arrays.asList(1,2,3).forEach(Foo::printIsEven) 
false 
true 
false 

技術上說,這不再是一個頂級的功能,但它達到了預期的效果。現在

,如果你知道這一點,還是要參考頂層方法...

據我所知道的,「頂級類」持有「狀態」的外殼是jdk.jshell.JShelljdk.jshell.JShell::printIsEven結果Error: invalid method reference。你已經提到它不可能使頂層方法靜態(Modifier 'static' not permitted in top-level declarations, ignored)。

一個快速瀏覽一下JEP後,似乎有意爲之。而它實際上提到從上面「定義靜態法,在新級」的做法。

猜測頂級「類」需要特殊的魔力能夠重新方法&其他頂級聲明和限制可能從JVM自身的侷限性,它能夠派生出在運行時重新定義類/方法The source很有趣,但我無法從中得出有意義的答案。


編輯:所以,我有點被帶走了。這是你的錯。
我仍然認爲這是不可能獲得的方法參考jshell一個最高級的方法,但是......我以前對原因的猜測爲什麼也許是錯誤的。

以下顯示在jshell中,當您「重新定義」一個類時,舊的類仍然存在:評估上下文僅改變某些映射以解析對新類定義的進一步引用。

jshell> class A { static int v=1; void m() { System.out.println(getClass() + " v=" + v); } } 
| created class A 

jshell> new A().m() 
class REPL.$JShell$11$A v=1 

// Changing static value of "v" 
jshell> class A { static int v=2; void m() { System.out.println(getClass() + " v=" + v); } } 
| modified class A 

// Actually not modified, this is still the same class (and as a result the static init of v has not been reexecuted, so, still 1) 
jshell> new A().m() 
class REPL.$JShell$11$A v=1 

// Let's add a boolean field to change the structure 
jshell> class A { static int v=3; boolean x=false; void m() { System.out.println(getClass() + " v=" + v); } } 
| replaced class A 

// Notice new class name: 
jshell> new A().m() 
class REPL.$JShell$11B$A v=3 

// But old version is still there, only hidden a bit by evaluation context: 
jshell> Class.forName("REPL.$JShell$11$A").getDeclaredField("v").getInt(null) 
$7 ==> 1 

jshell> Class.forName("REPL.$JShell$11B$A").getDeclaredField("v").getInt(null) 
$8 ==> 3 

所以這個小演示表明,它沒有任何與JVM內部類重新定義,因爲沒有這樣的事情發生在這裏。

然後我想看到所有加載的類的列表:

jshell> Class c = Thread.currentThread().getContextClassLoader().getClass() 
c ==> class jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader 

jshell> while (c != java.lang.ClassLoader.class) { c = c.getSuperclass(); System.out.println(c); } 
class java.net.URLClassLoader 
class java.security.SecureClassLoader 
class java.lang.ClassLoader 

jshell> c.getDeclaredField("classes").setAccessible(true) 
| java.lang.reflect.InaccessibleObjectException thrown: Unable to make field private final java.util.Vector java.lang.ClassLoader.classes accessible: module java.base does not "opens java.lang" to unnamed module @7494e528 
|  at AccessibleObject.checkCanSetAccessible (AccessibleObject.java:337) 
|  at AccessibleObject.checkCanSetAccessible (AccessibleObject.java:281) 
|  at Field.checkCanSetAccessible (Field.java:175) 
|  at Field.setAccessible (Field.java:169) 
|  at (#26:1) 

啊,是的,Java的9個模塊...該死:)

哦,那將是對所有今天。

+0

感謝您的深入跟蹤。我嘗試過'jshell -J - add-opens = java.base/java.lang = ALL-UNNAMED',甚至是'jshell -J - permit-illegal-access',並且在這兩種情況下仍然有'InaccessibleObjectException'。好吧。 – adashrod

相關問題