2017-05-29 41 views
10

Załóżmy zrobić to w jshell:Czy istnieje sposób użycia odwołań metod dla funkcji najwyższego poziomu w 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? 

W normalnym programie mógłbym napisać l.forEach(this::printIsEven) w non-statycznego kontekstu lub l.forEach(MyClass::printIsEven) w statycznej kontekście klasy o nazwie MyClass.

Korzystanie this::printIsEven w jshell nie działa, ponieważ jshell wykonuje instrukcje w statycznym kontekście, ale nie można używać statycznego Referencyjna metoda, ponieważ nie ma nazwę klasy poprzedzić ::printIsEven i próbuje l.forEach(::printIsEven) jest po prostu błąd składni.

+0

Nie użyłem JShella, ale czy nie można uczynić metody statyczną? –

+1

@ChandlerBing Nie, dzięki czemu 'Modifier 'static' jest niedozwolone w deklaracjach najwyższego poziomu, ignorowane' –

Odpowiedz

13

Można utworzyć nową klasę na to:

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 

Technicznie to nie jest funkcją najwyższego poziomu, ale osiąga pożądany efekt.

Teraz, jeśli wiedział, że i nadal chce odwołać metod najwyższego poziomu ...

O ile mogę powiedzieć, „klasy top-level”, która posiada „państwo” dla powłoki jest jdk.jshell.JShell , ale jdk.jshell.JShell::printIsEven jest wynikiem Error: invalid method reference. I już wspomniałeś, że nie jest możliwe uczynienie metod najwyższego poziomu statycznymi (Modifier 'static' not permitted in top-level declarations, ignored).

Po krótkim obejrzeniu JEP wydaje się celowe. I faktycznie wspomina o metodzie "określ-statyczną-metodę-w-nowej-klasie" z powyższego.

jestem zgadywania „klasy” top-level wymaga szczególnej magii, aby móc przedefiniować Metody & inne deklaracje najwyższym poziomie, a ograniczenia mogą wynikać z własnych ograniczeń JVM w swojej zdolności do przedefiniowanie klas/metod w środowisku wykonawczym. The source jest interesujący, ale nie jestem w stanie uzyskać na jego podstawie sensownej odpowiedzi.


Edytuj: Więc, trochę mnie to uszło. To Twoja wina.
Nadal uważam, że nie jest możliwe uzyskanie odwołania do metody do metody najwyższego poziomu w jshell, ale ... moje wcześniejsze przypuszczenie co do przyczyn prawdopodobnie jest złe.

Poniższe pokazuje, że w jshell, kiedy "redefiniujesz" klasę, stara klasa nadal tam jest: kontekst oceny przesuwa tylko niektóre odwzorowania, aby rozwiązać dalsze odniesienia do definicji nowej klasy.

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 

Więc ten mały demo sugeruje, że nie ma nic wspólnego z wewnętrznymi JVM dla klasy redefinicji, bo nic takiego się dzieje tutaj.

Potem chciałem zobaczyć listę wszystkich załadowanych klas:

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) 

Ach, tak, Jawa 9 modułów ... cholera :)

Och, dobrze, że będzie wszystko za dzisiaj.

+0

Dzięki za dogłębną obserwację. Próbowałem 'jshell -J - add-opens = java.base/java.lang = ALL-UNNAMED', a nawet' jshell -J - permit-illegal-access' i nadal otrzymałem 'InaccessibleObjectException' w obu przypadkach. No cóż. – adashrod