2017-05-29 77 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

相关问题