2017-05-29 39 views
10

Giả sử tôi làm điều này trong jshell:Có cách nào để sử dụng tham chiếu phương thức cho các hàm cấp cao nhất trong jshell không?

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? 

Trong một chương trình bình thường tôi có thể viết l.forEach(this::printIsEven) trong một bối cảnh không tĩnh hoặc l.forEach(MyClass::printIsEven) trong bối cảnh tĩnh của một lớp có tên MyClass.

Sử dụng this::printIsEven trong jshell không hoạt động vì jshell thực hiện báo cáo trong một bối cảnh tĩnh, nhưng bạn không thể sử dụng một tài liệu tham khảo phương pháp tĩnh vì không có tên lớp tiền tố ::printIsEven, và cố gắng l.forEach(::printIsEven) chỉ là một lỗi cú pháp.

+0

Tôi chưa sử dụng JShell, nhưng bạn không thể làm cho phương thức tĩnh? –

+1

@ChandlerBing Không, điều đó cho phép 'Modifier' static 'không được phép trong khai báo cấp cao nhất, bị bỏ qua' –

Trả lời

13

Bạn có thể tạo một lớp mới cho rằng:

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 

Về mặt kỹ thuật nó không còn là một chức năng cấp cao nhất, nhưng nó đạt được hiệu quả mong muốn.

Bây giờ, nếu bạn biết điều đó và vẫn muốn tham khảo các phương pháp top-level ...

Theo như tôi có thể nói, các "lớp học cấp cao nhất" chứa "nhà nước" cho vỏ là jdk.jshell.JShell , nhưng jdk.jshell.JShell::printIsEven kết quả trong số Error: invalid method reference. Và bạn đã đề cập đến việc không thể tạo các phương thức cấp cao tĩnh (Modifier 'static' not permitted in top-level declarations, ignored).

Sau khi xem nhanh JEP, có vẻ như cố ý. Và nó thực sự đề cập đến phương pháp "xác định-tĩnh-phương pháp-trong-lớp mới" từ trên.

Tôi đoán đầu cấp "lớp" cần đặc biệt kỳ diệu để có thể xác định lại phương pháp & tờ khai cấp cao khác, và những hạn chế có thể xuất phát từ những hạn chế riêng của JVM trong khả năng của mình để xác định lại các lớp/phương thức tại thời gian chạy. The source thú vị nhưng tôi không thể tìm ra câu trả lời có ý nghĩa từ đó.


Chỉnh sửa: Vì vậy, tôi đã bị mang đi. Đây là lỗi của bạn.
Tôi vẫn nghĩ rằng không thể có được phương pháp tham chiếu đến phương thức cấp cao nhất trong jshell, nhưng ... dự đoán trước đây của tôi về lý do có thể là sai.

Sau đây cho thấy rằng trong jshell, khi bạn "xác định lại" một lớp, lớp cũ vẫn còn đó: ngữ cảnh đánh giá chỉ thay đổi một số ánh xạ để giải quyết các tham chiếu thêm về định nghĩa lớp mới.

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 

Vì vậy, bản trình diễn nhỏ này cho thấy nó không liên quan gì đến nội bộ JVM để xác định lại lớp, vì không có điều gì xảy ra ở đây.

Sau đó, tôi muốn nhìn thấy danh sách của tất cả các lớp nạp:

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) 

Ah, vâng, Java 9 module ... chết tiệt :)

Ồ, đó sẽ là tất cả cho hôm nay.

+0

Cảm ơn bạn đã theo dõi sâu. Tôi đã thử 'jshell -J - add-opens = java.base/java.lang = ALL-UNNAMED' và thậm chí là' jshell -J - cho phép-bất hợp pháp-access' và vẫn nhận được 'InaccessibleObjectException' trong cả hai trường hợp. Ồ, tốt. – adashrod

Các vấn đề liên quan