From bfbaeb28b4e467b3af9f632e7747eeaaeeac63b2 Mon Sep 17 00:00:00 2001 From: Connor Schweighoefer Date: Fri, 31 May 2024 19:41:49 +0200 Subject: [PATCH 01/26] Add missing author entry --- app/data/authors.yaml | 11 +++++++++++ .../02_introduction-javafx-animation.md | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/data/authors.yaml b/app/data/authors.yaml index 7f4665e..28acb67 100644 --- a/app/data/authors.yaml +++ b/app/data/authors.yaml @@ -93,3 +93,14 @@ Marit is a Software Developer, a Java Champion and works as a Developer Advocate at JetBrains. She speaks at international conferences (including JavaZone, Devoxx, Voxxed Days, JSpring, JFall, JFokus and GOTO), local JUGs, podcasts and webinars, and has contributed to the book “97 Things Every Java Programmer Should Know” (O’Reilly Media). + +- name: Connor Schweighöfer + email: squidxtv@gmail.com + photo_url: https://squidxtv.me/img/Connor.png + github: SquidXTV + twitter: SquidXTV + website: https://squidxtv.me/ + description: | + Connor is a Java Developer and high school student from Germany with four years of programming experience. + He is a top helper and community lead in Together Java, one of the largest Java communities on Discord, with over 30,000 users. + Connor has worked on various Java projects, including Discord bots, Minecraft plugins and JavaFX applications. diff --git a/app/pages/learn/01_tutorial/07_rich_client_apps/02_introduction-javafx-animation.md b/app/pages/learn/01_tutorial/07_rich_client_apps/02_introduction-javafx-animation.md index 4d39cf2..b147235 100644 --- a/app/pages/learn/01_tutorial/07_rich_client_apps/02_introduction-javafx-animation.md +++ b/app/pages/learn/01_tutorial/07_rich_client_apps/02_introduction-javafx-animation.md @@ -10,8 +10,10 @@ layout: learn/tutorial.html subheader_select: tutorials main_css_id: learn description: "Learn to create advanced JavaFX animations" -last_update: 2024-05-22 +last_update: 2024-05-31 +author: ["ConnorSchweighöfer"] --- +
The [javafx.animation](javafxdoc:AnimationPackageSummary) package offers a simple framework for creating animations and transitions in a JavaFX application. It operates on the principle of [`WritableValue`](javafxdoc:WritableValue), which is used across JavaFX. `WritableValue` is an interface that wraps a value that can be read and set. From 6e4688a4b27efb7b5cf2ebdf13318bba73b023c8 Mon Sep 17 00:00:00 2001 From: Chad Arimura Date: Fri, 31 May 2024 12:38:37 -0700 Subject: [PATCH 02/26] syncing javadoc files --- app/data/javadoc.json | 68 +++++++++++++++++++++++++++++++++-------- app/data/javafxdoc.json | 9 +++--- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/app/data/javadoc.json b/app/data/javadoc.json index c358cf4..a4e40f1 100644 --- a/app/data/javadoc.json +++ b/app/data/javadoc.json @@ -1,5 +1,5 @@ { - "current_release": "19", + "current_release": "22", "java-documentation": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/", "javadoc_root": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/api/", @@ -13,7 +13,7 @@ "jpackage": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jpackage.html", "java-api-docs": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/api/index.html", "specification": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/api/index.html", - "release-notes": "https://www.oracle.com/java/technologies/javase/@@CURRENT_RELEASE@@-relnotes.html", + "release-notes": "https://www.oracle.com/java/technologies/javase/@@CURRENT_RELEASE@@u-relnotes.html", "jvm-guide": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/vm/java-virtual-machine-technology-overview.html", "gc-tuning": { "link": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/gctuning/introduction-garbage-collection-tuning.html", @@ -52,6 +52,7 @@ "jmod": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jmod.html", "jdeps": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jdeps.html", "jdeprscan": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jdeprscan.html", + "jwebserver": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jwebserver.html", "jfr": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jfr.html", "jconsole": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jconsole.html", "jps": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jps.html", @@ -77,6 +78,10 @@ "rfc-5246": "https://www.ietf.org/rfc/rfc5246.html", "rfc-8446": "https://www.ietf.org/rfc/rfc8446.html", "rfc-8017": "https://www.ietf.org/rfc/rfc8017.html", + "rfc-8032": "https://tools.ietf.org/html/rfc8032", + "trec-x509": "https://www.itu.int/rec/T-REC-X.509/en", + "dss": "https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf", + "rfc-1422": "https://www.ietf.org/rfc/rfc1422.html", "ANSI-20X9.62": "https://standards.globalspec.com/std/1955141/ANSI%20X9.62", "ide-eclipse": "https://www.eclipseide.org/", @@ -121,6 +126,8 @@ "Object.wait(long)": "java.base/java/lang/Object.html#wait(long)", "Object.wait(long,int)": "java.base/java/lang/Object.html#wait(long,int)", + "ClassLoader": "java.base/java/lang/ClassLoader.html", + "NullPointerException": "java.base/java/lang/NullPointerException.html", "Override": "java.base/java/lang/Override.html", @@ -156,8 +163,8 @@ "Long": "java.base/java/lang/Long.html", "Long.compareTo(Long)": "java.base/java/lang/Long.html#compareTo(java.lang.Long)", - "Long.compareUnsigned(int,int)": "java.base/java/lang/Long.html#compareUnsigned(int,int)", - "Long.divideUnsigned(int,int)": "java.base/java/lang/Long.html#divideUnsigned(int,int)", + "Long.compareUnsigned(long,long)": "java.base/java/lang/Long.html#compareUnsigned(long,long)", + "Long.divideUnsigned(long,long)": "java.base/java/lang/Long.html#divideUnsigned(long,long)", "Arrays": "java.base/java/util/Arrays.html", "Arrays.asList()": "java.base/java/util/Arrays.html#asList(T...)", @@ -237,10 +244,6 @@ "Math.toRadians(double)": "java.base/java/lang/Math.html#toRadians(double)", "Math": "java.base/java/lang/Math.html", - "Collections.sort(List)": "java.base/java/util/Collections.html#sort(java.util.List)", - "Collections.sort(List,comparator)": "java.base/java/util/Collections.html#sort(java.util.List,java.util.Comparator)", - "Collections.emptyList()": "java.base/java/util/Collections.html#emptyList()", - "File": "java.base/java/io/File.html", "File(String)": "java.base/java/io/File.html#%3Cinit%3E(java.lang.String)", "File(String,String)" : "java.base/java/io/File.html#%3Cinit%3E(java.lang.String,java.lang.String)", @@ -275,6 +278,7 @@ "File.toURI()" : "java.base/java/io/File.html#toURI()", "File.isHidden()" : "java.base/java/io/File.html#isHidden()", "File.list()" : "java.base/java/io/File.html#list()", + "File.listFiles()" : "java.base/java/io/File.html#listFiles()", "File.mkdir()" : "java.base/java/io/File.html#mkdir()", "File.mkdirs()" : "java.base/java/io/File.html#mkdirs()", "File.listRoots()" : "java.base/java/io/File.html#listRoots()", @@ -314,6 +318,8 @@ "Files.readAllLines(Path)": "java.base/java/nio/file/Files.html#readAllLines(java.nio.file.Path)", "Files.readAllLines(Path,Charset)": "java.base/java/nio/file/Files.html#readAllLines(java.nio.file.Path,java.nio.charset.Charset)", "Files.write(byte)": "java.base/java/nio/file/Files.html#write(java.nio.file.Path,byte%5B%5D,java.nio.file.OpenOption...)", + "Files.writeString()": "java.base/java/nio/file/Files.html#writeString(java.nio.file.Path,java.lang.CharSequence,java.nio.file.OpenOption...)", + "Files.write()": "java.base/java/nio/file/Files.html#write(java.nio.file.Path,byte%5B%5D,java.nio.file.OpenOption...)", "Files.write(Iterable)": "java.base/java/nio/file/Files.html#write(java.nio.file.Path,java.lang.Iterable,java.nio.charset.Charset,java.nio.file.OpenOption...)", "Files.newBufferedReader(CharSet)": "java.base/java/nio/file/Files.html#newBufferedReader(java.nio.file.Path,java.nio.charset.Charset)", "Files.newBufferedWriter()": "java.base/java/nio/file/Files.html#newBufferedWriter(java.nio.file.Path,java.nio.charset.Charset,java.nio.file.OpenOption...)", @@ -335,9 +341,16 @@ "Files.readSymbolicLink(Path)": "java.base/java/nio/file/Files.html#readSymbolicLink(java.nio.file.Path)", "Files.probeContentType(Path)": "java.base/java/nio/file/Files.html#probeContentType(java.nio.file.Path)", "Files.lines(Path)": "java.base/java/nio/file/Files.html#lines(java.nio.file.Path)", + "Files.list(Path)": "java.base/java/nio/file/Files.html#list(java.nio.file.Path)", + "Files.find(Path)": "java.base/java/nio/file/Files.html#find(java.nio.file.Path,int,java.util.function.BiPredicate,java.nio.file.FileVisitOption...)", + "Files.walk(Path)": "java.base/java/nio/file/Files.html#walk(java.nio.file.Path,java.nio.file.FileVisitOption...)", + "Files.walk(Path,depth)": "java.base/java/nio/file/Files.html#walk(java.nio.file.Path,int,java.nio.file.FileVisitOption...)", + "Files.walkFileTree(Path)": "java.base/java/nio/file/Files.html#walkFileTree(java.nio.file.Path,java.nio.file.FileVisitor)", "Files.copy(InputStream,Path,CopyOption)": "java.base/java/nio/file/Files.html#copy(java.io.InputStream,java.nio.file.Path,java.nio.file.CopyOption...)", "Files.copy(Path,OutputStream)": "java.base/java/nio/file/Files.html#copy(java.nio.file.Path,java.io.OutputStream)", "Files.copy(Path,Path,CopyOption)": "java.base/java/nio/file/Files.html#copy(java.nio.file.Path,java.nio.file.Path,java.nio.file.CopyOption...)", + "Files.readString(Path)": "java.base/java/nio/file/Files.html#readString(java.nio.file.Path)", + "Files.readAllBytes(Path)": "java.base/java/nio/file/Files.html#readAllBytes(java.nio.file.Path)", "Files.setAttribute(Path,String,Object,LinkOption)": "java.base/java/nio/file/Files.html#setAttribute(java.nio.file.Path,java.lang.String,java.lang.Object,java.nio.file.LinkOption...)", "Files.createTempFile(String,String,FileAttribute)": "java.base/java/nio/file/Files.html#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute...)", "Files.createTempFile(Path,String,String,FileAttribute)": "java.base/java/nio/file/Files.html#createTempFile(java.nio.file.Path,java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute...)", @@ -451,6 +464,8 @@ "Float.parseFloat(String)": "java.base/java/lang/Float.html#parseFloat(java.lang.String)", "Double": "java.base/java/lang/Double.html", + "Double.toString(double)": "java.base/java/lang/Double.html#Double.html#toString(double)", + "Double.parseDouble(String)": "java.base/java/lang/Double.html#parseDouble(java.lang.String)", "Double.compareTo(Double)": "java.base/java/lang/Double.html#compareTo(java.lang.Double)", "Character": "java.base/java/lang/Character.html", @@ -566,6 +581,7 @@ "LineNumberReader": "java.base/java/io/LineNumberReader.html", "BufferedWriter": "java.base/java/io/BufferedWriter.html", + "BufferedWriter.write(int)": "java.base/java/io/BufferedWriter.html#write(int)", "BufferedWriter.close()": "java.base/java/io/BufferedWriter.html#close()", "FileNotFoundException": "java.base/java/io/FileNotFoundException.html", @@ -601,10 +617,14 @@ "BufferedOutputStream": "java.base/java/io/BufferedOutputStream.html", "PrintWriter": "java.base/java/io/PrintWriter.html", + "PrintWriter.printf()": "java.base/java/io/PrintWriter.html#printf(java.lang.String,java.lang.Object...)", "PrintWriter.println()": "java.base/java/io/PrintWriter.html#println()", - "GZIPInputStream": "java.base/java/io/GZIPInputStream.html", - "GZIPOutputStream": "java.base/java/io/GZIPOutputStream.html", + "ZipInputStream": "java.base/java/util/zip/ZipInputStream.html", + "ZipOutputStream": "java.base/java/util/zip/ZipOutputStream.html", + + "GZIPInputStream": "java.base/java/util/zip/GZIPInputStream.html", + "GZIPOutputStream": "java.base/java/util/zip/GZIPOutputStream.html", "DataOutputStream": "java.base/java/io/DataOutputStream.html", "DataOutputStream.writeBoolean(boolean)": "java.base/java/io/DataOutputStream#writeBoolean(boolean)", @@ -641,6 +661,8 @@ "InputStreamReader": "java.base/java/io/InputStreamReader.html", "OutputStreamWriter": "java.base/java/io/OutputStreamWriter.html", + "Scanner": "java.base/java/util/Scanner.html", + "Closeable": "java.base/java/io/Closeable.html", "Closeable.close()": "java.base/java/io/Closeable.html#close()", @@ -670,6 +692,8 @@ "SeekableByteChannel.write(ByteBuffer)": "java.base/java/nio/channels/SeekableByteChannel.html#write(java.nio.ByteBuffer)", "SeekableByteChannel.truncate(long)": "java.base/java/nio/channels/SeekableByteChannel.html#truncate(long)", + "ImageIO.read(URL)": "java.desktop/javax/imageio/ImageIO.html#read(java.net.URL)", + "URI": "java.base/java/net/URI.html", "LinkedList": "java.base/java/util/LinkedList.html", @@ -949,6 +973,7 @@ "NegativeArraySizeException": "java.base/java/lang/NegativeArraySizeException.html", "EmptyStackException": "java.base/java/util/EmptyStackException.html", "RuntimeException": "java.base/java/lang/RuntimeException.html", + "ReflectiveOperationException": "java.base/java/lang/ReflectiveOperationException.html", "ArrayStoreException": "java.base/java/lang/ArrayStoreException.html", "ClassCastException": "java.base/java/lang/ClassCastException.html", @@ -1054,6 +1079,9 @@ "ArrayDeque": "java.base/java/util/ArrayDeque.html", + "Iterator": "java.base/java/util/Iterator.html", + "Iterator.forEachRemaining(Consumer)": "java.base/java/util/Iterator.html#forEachRemaining(java.util.function.Consumer)", + "Collection": "java.base/java/util/Collection.html", "Collection.removeIf(Predicate)": "java.base/java/util/Collection.html#removeIf(java.util.function.Predicate)", "Collection.add(E)": "java.base/java/util/Collection.html#add(E)", @@ -1078,8 +1106,11 @@ "Collections.min(Collection)": "java.base/java/util/Collections.html#min(java.util.Collection)", "Collections.reverse(List)": "java.base/java/util/Collections.html#reverse(java.util.List)", "Collections.rotate(List,int)": "java.base/java/util/Collections.html#rotate(java.util.List,int)", - "Collections.shuffle(List)": "java.base/java/util/Collections.html#shuffle(java.util.List)", "Collections.swap(List,int,int)": "java.base/java/util/Collections.html#swap(java.util.List,int,int)", + "Collections.sort(List)": "java.base/java/util/Collections.html#sort(java.util.List)", + "Collections.shuffle(List)": "java.base/java/util/Collections.html#shuffle(java.util.List)", + "Collections.sort(List,comparator)": "java.base/java/util/Collections.html#sort(java.util.List,java.util.Comparator)", + "Collections.emptyList()": "java.base/java/util/Collections.html#emptyList()", "Deque": "java.base/java/util/Deque.html", "Deque.addFirst(E)": "java.base/java/util/Deque.html#addFirst(E)", @@ -1391,6 +1422,7 @@ "Stream.toArray()": "java.base/java/util/stream/Stream.html#toArray()", "Stream.toList()": "java.base/java/util/stream/Stream.html#toList()", + "HttpClient": "java.net.http/java/net/http/HttpClient.html", "HttpClient.send(HttpRequest,HttpResponse.BodyHandler)": "java.net.http/java/net/http/HttpClient.html#send(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler)", "HttpResponse.BodyHandlers.ofLines()": "java.net.http/java/net/http/HttpResponse.BodyHandlers.html#ofLines()", @@ -1406,6 +1438,16 @@ "MessageDigest": "java.base/java/security/MessageDigest.html", "Security.getProviders()": "java.base/java/security/Security.html#getProviders()", "Security.addProvider(java.security.Provider)": "java.base/java/security/Security.html#addProvider(java.security.Provider)", - "Security.insertProviderAt": "java.base/java/security/Security.html#insertProviderAt(java.security.Provider,int)" - + "Security.insertProviderAt": "java.base/java/security/Security.html#insertProviderAt(java.security.Provider,int)", + "Certificate": "java.base/java/security/cert/Certificate.html", + "X509Certificate": "java.base/java/security/cert/X509Certificate.html", + "TrustAnchor": "java.base/java/security/cert/TrustAnchor.html", + "CertPathValidator": "java.base/java/security/cert/CertPathValidator.html", + "Security": "java.base/java/security/Security.html", + "FlightRecorderMXBean": "jdk.management.jfr/jdk/management/jfr/FlightRecorderMXBean.html", + "MBeanServerConnection": "java.management/javax/management/MBeanServerConnection.html", + "SimpleFileServer": "jdk.httpserver/com/sun/net/httpserver/SimpleFileServer", + + "Method": "java.base/java/lang/reflect/Method.html", + "Method.invoke()": "java.base/java/lang/reflect/Method.html#invoke(java.lang.Object,java.lang.Object...)" } \ No newline at end of file diff --git a/app/data/javafxdoc.json b/app/data/javafxdoc.json index ec8ef57..2cef141 100644 --- a/app/data/javafxdoc.json +++ b/app/data/javafxdoc.json @@ -1,8 +1,8 @@ { - "current_release": "19", + "current_release": "21.0.1", + "release_uuid": "e5ab43c6aed54893b0840c1f2dcfca4d", - "javafx-documentation": "https://openjfx.io/javadoc/@@CURRENT_RELEASE@@/", - "javafxdoc_root": "https://openjfx.io/javadoc/@@CURRENT_RELEASE@@/", + "javafxdoc_root": "https://download.java.net/java/GA/javafx@@CURRENT_RELEASE@@/@@RELEASE_UUID@@/docs/api/", "Application": "javafx.graphics/javafx/application/Application.html", @@ -54,7 +54,6 @@ "FXMLLoader": "javafx.fxml/javafx/fxml/FXMLLoader.html", "Initializable": "javafx.fxml/javafx/fxml/Initializable.html", - "AnimationPackageSummary": "javafx.graphics/javafx/animation/package-summary", "WritableValue": "javafx.base/javafx/beans/value/WritableValue", "Animation": "javafx.graphics/javafx/animation/Animation", @@ -78,4 +77,4 @@ "Interpolator.SPLINE": "javafx.graphics/javafx/animation/Interpolator#SPLINE(double,double,double,double)", "Interpolator.TANGENT": "javafx.graphics/javafx/animation/Interpolator#TANGENT(javafx.util.Duration,double,javafx.util.Duration,double)", "AnimationTimer": "javafx.graphics/javafx/animation/AnimationTimer" -} +} \ No newline at end of file From 3df93237bf1a35b102dfb7f65c27d9a2244e53e5 Mon Sep 17 00:00:00 2001 From: Chad Arimura Date: Fri, 31 May 2024 15:10:54 -0700 Subject: [PATCH 03/26] fixing links --- app/data/javafxdoc.json | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/app/data/javafxdoc.json b/app/data/javafxdoc.json index 2cef141..557e45a 100644 --- a/app/data/javafxdoc.json +++ b/app/data/javafxdoc.json @@ -54,27 +54,27 @@ "FXMLLoader": "javafx.fxml/javafx/fxml/FXMLLoader.html", "Initializable": "javafx.fxml/javafx/fxml/Initializable.html", - "AnimationPackageSummary": "javafx.graphics/javafx/animation/package-summary", - "WritableValue": "javafx.base/javafx/beans/value/WritableValue", - "Animation": "javafx.graphics/javafx/animation/Animation", - "Duration": "javafx.base/javafx/util/Duration", - "Node": "javafx.graphics/javafx/scene/Node", - "FadeTransition": "javafx.graphics/javafx/animation/FadeTransition", - "FillTransition": "javafx.graphics/javafx/animation/FillTransition", - "PathTransition": "javafx.graphics/javafx/animation/PathTransition", - "ScaleTransition": "javafx.graphics/javafx/animation/ScaleTransition", - "StrokeTransition": "javafx.graphics/javafx/animation/StrokeTransition", - "TranslateTransition": "javafx.graphics/javafx/animation/TranslateTransition", - "Timeline": "javafx.graphics/javafx/animation/Timeline", - "KeyFrame": "javafx.graphics/javafx/animation/KeyFrame", - "KeyValue": "javafx.graphics/javafx/animation/KeyValue", - "Interpolator": "javafx.graphics/javafx/animation/Interpolator", - "Interpolator.DISCRETE": "javafx.graphics/javafx/animation/Interpolator#DISCRETE", - "Interpolator.LINEAR": "javafx.graphics/javafx/animation/Interpolator#LINEAR", - "Interpolator.EASE_IN": "javafx.graphics/javafx/animation/Interpolator#EASE_IN", - "Interpolator.EASE_OUT": "javafx.graphics/javafx/animation/Interpolator#EASE_OUT", - "Interpolator.EASE_BOTH": "javafx.graphics/javafx/animation/Interpolator#EASE_BOTH", - "Interpolator.SPLINE": "javafx.graphics/javafx/animation/Interpolator#SPLINE(double,double,double,double)", - "Interpolator.TANGENT": "javafx.graphics/javafx/animation/Interpolator#TANGENT(javafx.util.Duration,double,javafx.util.Duration,double)", - "AnimationTimer": "javafx.graphics/javafx/animation/AnimationTimer" + "AnimationPackageSummary": "javafx.graphics/javafx/animation/package-summary.html", + "WritableValue": "javafx.base/javafx/beans/value/WritableValue.html", + "Animation": "javafx.graphics/javafx/animation/Animation.html", + "Duration": "javafx.base/javafx/util/Duration.html", + "Node": "javafx.graphics/javafx/scene/Node.html", + "FadeTransition": "javafx.graphics/javafx/animation/FadeTransition.html", + "FillTransition": "javafx.graphics/javafx/animation/FillTransition.html", + "PathTransition": "javafx.graphics/javafx/animation/PathTransition.html", + "ScaleTransition": "javafx.graphics/javafx/animation/ScaleTransition.html", + "StrokeTransition": "javafx.graphics/javafx/animation/StrokeTransition.html", + "TranslateTransition": "javafx.graphics/javafx/animation/TranslateTransition.html", + "Timeline": "javafx.graphics/javafx/animation/Timeline.html", + "KeyFrame": "javafx.graphics/javafx/animation/KeyFrame.html", + "KeyValue": "javafx.graphics/javafx/animation/KeyValue.html", + "Interpolator": "javafx.graphics/javafx/animation/Interpolator.html", + "Interpolator.DISCRETE": "javafx.graphics/javafx/animation/Interpolator.html#DISCRETE", + "Interpolator.LINEAR": "javafx.graphics/javafx/animation/Interpolator.html#LINEAR", + "Interpolator.EASE_IN": "javafx.graphics/javafx/animation/Interpolator.html#EASE_IN", + "Interpolator.EASE_OUT": "javafx.graphics/javafx/animation/Interpolator.html#EASE_OUT", + "Interpolator.EASE_BOTH": "javafx.graphics/javafx/animation/Interpolator.html#EASE_BOTH", + "Interpolator.SPLINE": "javafx.graphics/javafx/animation/Interpolator.html#SPLINE(double,double,double,double)", + "Interpolator.TANGENT": "javafx.graphics/javafx/animation/Interpolator.html#TANGENT(javafx.util.Duration,double,javafx.util.Duration,double)", + "AnimationTimer": "javafx.graphics/javafx/animation/AnimationTimer.html" } \ No newline at end of file From 6758e2b9693421527d43fed8a6b269a320186a3d Mon Sep 17 00:00:00 2001 From: Ana Date: Sat, 1 Jun 2024 18:22:13 +0200 Subject: [PATCH 04/26] Add missing code for processing JavaFx documentation. --- gulpfile.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 1287329..6b96828 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -226,9 +226,9 @@ function pages() { if (href.includes("javafxdoc:")) { let javafxdoc_id = href.match(/javafxdoc:([\w\.\-(),]+)/) if (javafxdoc_id != null) { - processedHref = processDocLink(javafxdoc["javafxdoc_root"] + javafxdoc[javafxdoc_id[1]]); + processedHref = processJavaFxDocLink(javafxdoc["javafxdoc_root"] + javafxdoc[javafxdoc_id[1]]); if (processedHref.includes("undefined")) { - console.log("Javadoc " + javafxdoc_id[1] + " resolved to undefined"); + console.log("JavaFX Javadoc " + javafxdoc_id[1] + " resolved to undefined"); } return `${text}`; } @@ -402,6 +402,10 @@ function processDocLink(link) { return link.replace("@@CURRENT_RELEASE@@", javadoc[`current_release`]); } +function processJavaFxDocLink(link) { + return link.replace("@@CURRENT_RELEASE@@", javafxdoc[`current_release`]).replace("@@RELEASE_UUID@@", javafxdoc[`release_uuid`]); +} + function is_tutorial(file) { return file.fm.type == "tutorial" || file.fm.type == "tutorial-group"; From 2b081bc94f579e1a5afb59c144f41aecd106d333 Mon Sep 17 00:00:00 2001 From: Nataliia Dziubenko Date: Fri, 16 Feb 2024 15:37:14 +0100 Subject: [PATCH 05/26] Introduction to Method Handles tutorial --- app/data/authors.yaml | 9 +- .../02_invoke/00_methodhandle.md | 531 ++++++++++++++++++ 2 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md diff --git a/app/data/authors.yaml b/app/data/authors.yaml index 28acb67..73fb847 100644 --- a/app/data/authors.yaml +++ b/app/data/authors.yaml @@ -20,7 +20,6 @@ moderator on CodeRanch which is the best java discussion forum on the web. She has moderated on topics including JDBC, Testing, IDEs, Process and Performance. She is one of the leaders of javasig.com and mentors a high school robotics team in her free time. - - name: Cay Horstmann email: cay@horstmann.com @@ -104,3 +103,11 @@ Connor is a Java Developer and high school student from Germany with four years of programming experience. He is a top helper and community lead in Together Java, one of the largest Java communities on Discord, with over 30,000 users. Connor has worked on various Java projects, including Discord bots, Minecraft plugins and JavaFX applications. + +- name: Nataliia Dziubenko + github: smthelusive + twitter: worth_exploring + photo_url: https://nataliiadziubenko.com/assets/images/nataliia.png + website: https://medium.com/@nataliiadziubenko + description: | + Nataliia is a Software Engineer passionate about Java & JVM. \ No newline at end of file diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md new file mode 100644 index 0000000..b6ed63a --- /dev/null +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -0,0 +1,531 @@ +--- +id: api.invoke +title: "Introduction to Method Handles" +slug: learn/introduction_to_method_handles +type: tutorial +category: api +category_order: 1 +layout: learn/tutorial.html +subheader_select: tutorials +main_css_id: learn +description: "What is Method Handle mechanism, how is it different from Reflection API, and what tooling does it provide." +author: ["NataliiaDziubenko"] +toc: + - What are Method Handles {intro} + - Access checking {access} + - Method handle lookup {lookup} + - Method type {methodtype} + - Method handle invocation {invocation} + - Accessing fields {fields} + - Working with arrays {arrays} + - Method handles vs Reflection {vsreflection} + - Conversion between Reflection API and method handles {unreflect} + - Exception handling {exceptions} + - Method handle transformations {transformations} + - Conclusion {conclusion} +last_update: 2024-04-29 +--- + +  +## What are Method Handles +Method Handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, but in fact, these two APIs serve different purposes. +There are some overlapping capabilities that both Reflection API and Method Handles can provide. +For example, method invocation, where Method Handles perform better. + +What exactly is a Method Handle? It's a direct reference to a method, constructor, or field, which can be invoked. +Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the arguments, or transform the return values, for example. + +Let's take a closer look at what Method Handle mechanism can provide and how we can effectively use it. + +  +## Access checking +The access checking for method handle invocations is done differently compared to the Reflection API. +With reflection, each call results in access checks for the caller. +For method handles, the access is only checked when the method handle is created. +The access level that it has can't exceed the access level of the class where it's created; however, it can be further limited. + +It is important to keep in mind, that if the method handle is created within a context that can access non-public members, when passed outside, it can still access non-public members. +As a result, non-public members can potentially be executed from code where they shouldn't be accessible. +It's a developer's responsibility to keep such method handles private to their context. + +  +## Method handle lookup +To create a method handle, we first need to create a `Lookup` object, which acts as a factory for creating method handles. +At this point, we can determine our level of trust in this lookup object based on our knowledge of how it will be used. +Will this lookup object or the method handles be passed somewhere else? + +If we don't fully trust our lookup object, we can create it using `MethodHandles::publicLookup` method, so it will only be able to +search for public members in public classes within packages that are exported unconditionally: + +```java +MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); +``` + +It's possible to create a lookup object that can create method handles for any members, including private and protected ones: + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +``` + +  +## Method type +To be able to look up a method handle, we also need to provide the type information of the method or field. +The method type information is represented as `MethodType` object. +To instantiate a `MethodType`, we have to provide the return type as the first parameter followed by all the argument types: + +```java +MethodType methodType = MethodType.methodType(int.class /* the method returns integer */, + String.class /* and accepts a single String argument*/); +``` + +Having the `Lookup` and the `MethodType` instances, we can look up the method handle. +For virtual methods, we should use `findVirtual`, and for static methods `findStatic`. +Both these methods accept the following arguments: a `Class`, where the method is located, a method name represented as a `String`, and a `MethodType` instance. + +In the example below, we are looking up a virtual method `String::replace`, which accepts two `char` arguments and returns a `String`: + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); +MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); +``` + +In the next example, we are looking up a static method `String::valueOf`, which accepts an `Object` and returns a `String`: + +```java +MethodType valueOfMethodType = MethodType.methodType(String.class, Object.class); +MethodHandle valueOfMethodHandle = lookup.findStatic(String.class, "valueOf", valueOfMethodType); +``` + +Similarly, we could use `findConstructor` method to look up a method handle pointing to any constructor. + +Finally, when we have obtained a method handle, we can invoke the underlying method. + +  +## Method handle invocation +The invocation can also be done in multiple ways. + +All the methods that facilitate invocation eventually funnel down to a single method that is called in the end: `MethodHandle::invokeExact`. +As the method name suggests, the arguments provided to `invokeExact` method must strictly match the method handle's type. + +For example, if we invoke a `String::replace` method, the arguments must strictly correspond to a `String` return type and two `char` arguments: + +```java +MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); +MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); +String result = (String) replaceMethodHandle.invokeExact("dummy", 'd', 'm'); +``` + +`MethodHandle.invoke` is more permissive. It attempts to obtain a new method handle with adjusted types that would strictly match the types of provided arguments. +After that, it will be able to invoke the adjusted method handle using `invokeExact`. + +```java +MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); +MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); +String result = (String) replaceMethodHandle.invoke((Object)"dummy", (Object)'d', (Object)'m'); // would fail with `invokeExact` +``` + +One other alternative to invoke a method handle is to use `MethodHandle::invokeWithArguments`. +The result of this method invocation is equivalent to `invoke`, with the only difference that all the arguments can be provided as an array or list of objects. + +One interesting feature of this method is that if the number of provided arguments exceeds the expected number, +all the leftover arguments will be squashed into the last argument, which will be treated as an array. + +  +## Accessing fields +It is possible to create method handles that have read or write access to fields. +For instance fields, this is facilitated by `findGetter` and `findSetter` methods, and for static fields, by `findStaticGetter` and `findStaticSetter` methods. +We don't need to provide a `MethodType` instance; instead, we should provide a single type, which is the type of the field. + +For example, if we have a static field `magic` in our `Example` class: + +```java +private static String magic = "initial value static field"; +``` + +Given that we have created a `Lookup` object + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +``` + +We can simply create both setter and getter method handles and invoke them separately: + +```java +MethodHandle setterStaticMethodHandle = lookup.findStaticSetter(Example.class, "magic", String.class); +MethodHandle getterStaticMethodHandle = lookup.findStaticGetter(Example.class, "magic", String.class); + +setterStaticMethodHandle.invoke("new value static field"); +String staticFieldResult = (String) getterStaticMethodHandle.invoke(); // staticFieldResult == `new value static field` +``` + +Here is an instance field `abc` of class `Example`: + +```java +private String abc = "initial value"; +``` + +We can similarly create method handles for reading and writing to the instance field: + +```java +MethodHandle setterMethodHandle = lookup.findSetter(Example.class, "abc", String.class); +MethodHandle getterMethodHandle = lookup.findGetter(Example.class, "abc", String.class); +``` + +To use setter and getter method handles with an instance field, we must first obtain an instance of the class where the field belongs: + +```java +Example example = new Example(); +``` + +Afterward, we must provide an instance of `Example` for invocation of our setter and getter: + +```java +setterMethodHandle.invoke(example, "new value"); +String result = (String) getterMethodHandle.invoke(example); // result == `new value` +``` + +Although it is possible to read and write field values using method handles, it's not common practice. +For fields, it's more suitable to use `VarHandle`s instead, which can be created using `findVarHandle` and `findStaticVarHandle` methods. + +  +## Working with arrays +The `MethodHandles` class contains certain methods that provide a number of preset method handles. +These include method handles that allow array manipulations. Creating these method handles doesn't require access checking, so the lookup object is not necessary. + +Let's create an array of Strings containing 5 elements using `arrayConstructor`: + +```java +MethodHandle arrayConstructor = MethodHandles.arrayConstructor(String[].class); +String[] arr = (String[]) arrayConstructor.invoke(5); +``` + +To modify a single element, we can use `arrayElementSetter`, to which we provide the reference to the target array, the index of an element, and the new value: + +```java +MethodHandle elementSetter = MethodHandles.arrayElementSetter(String[].class); +elementSetter.invoke(arr, 4, "test"); +``` + +To read the value of a single element, we should use `arrayElementGetter` method handle, to which we provide the reference to an array and the element index: + +```java +MethodHandle elementGetter = MethodHandles.arrayElementGetter(String[].class); +String element = (String) elementGetter.invoke(arr, 4); // element == "test" +``` + +We could also use the method handle provided by `arrayLength` to get the array length as integer: + +```java +MethodHandle arrayLength = MethodHandles.arrayLength(String[].class); +int length = (int) arrayLength.invoke(arr); // length == 5 +``` + +  +## Method handles vs Reflection +The Method Handles mechanism was not designed for wide usage. Originally, it was meant as a tool to assist compiler and language runtime developers. +It has a narrower range of capabilities than the Reflection API. +For example, the Reflection API allows listing the class members and inspecting their properties, which cannot be done using method handles. + +There are also certain differences when it comes to access checking and security considerations. +The Core Reflection API performs access checking against every caller, on every call, while for method handles, access is checked only during construction. +This makes invocation through method handles faster than through reflection. +However, certain precautions have to be taken so the method handle is not passed to the code where it shouldn't be accessible. + +Method handles and reflection serve different purposes, so it's important to pick the right tool based on your needs. + +  +## Conversion between Reflection API and method handles +The `Lookup` object can be used to convert Reflection API objects to behaviorally equivalent method handles, which provide more direct and efficient access to the underlying class members. + +To create a method handle pointing to a given `Method` (given that the lookup class has permission to do so), we can use `unreflect`. + +Let's say we have a `test` method in our `Example` class which accepts a `String` argument and returns a `String`. +Using the Reflection API, we can obtain a `Method` object: + +```java +Class clazz = Class.forName("Example"); +Method method = clazz.getMethod("test", String.class); +``` + +With the help of the lookup object, we can `unreflect` the `Method` object to obtain a `MethodHandle`: + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +MethodHandle methodHandle = lookup.unreflect(method); +String result = (String) methodHandle.invoke("something"); +``` + +Similarly, given a `Field` object, we can obtain getter and setter method handles: + +```java +Field field = clazz.getField("magic"); +MethodHandle setterMethodHandle = lookup.unreflectSetter(field); +MethodHandle getterMethodHandle = lookup.unreflectGetter(field); +setterMethodHandle.invoke("something"); +String result = (String) getterMethodHandle.invoke(); // result == "something" +``` + +Conversion from `MethodHandle` to a `Member` is also possible, with the condition that no transformations have been performed to the given `MethodHandle`. + +Let's say we have a method handle pointing directly to a method. We can use the `MethodHandles::reflectAs` method to obtain the `Method` object: + +```java +Method method = MethodHandles.reflectAs(Method.class, methodHandle); +``` + +It works similarly for the `Field` object: + +```java +Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same result is achieved by reflecting `setterMethodHandle` +``` + +  +## Exception handling + +Both `invokeExact` and `invoke` throw `Throwable`, so there is no limitation to what an underlying method can throw. +The method that invokes a method handle must either explicitly throw a `Throwable` or catch it. + +There are certain methods in the `MethodHandles` API that can make exception handling easier. +Let's take a look at several examples. + +### `catch` wrapper +`MethodHandles.catchException` method can wrap a given method handle inside a provided exception handler method handle. + +Say, we have a method `problematicMethod` that does some job, and a method `exceptionHandler` that handles a particular exception `IllegalArgumentException`. +The exception handler method must return the same type as the original method. +The first argument it accepts is a `Throwable` that we're interested in, after which follow the rest of the arguments that we've originally accepted: + +```java +public static int problematicMethod(String argument) throws IllegalArgumentException { + if ("invalid".equals(argument)) { + throw new IllegalArgumentException(); + } + return 1; +} + +public static int exceptionHandler(IllegalArgumentException e, String argument) { + // log exception + return 0; +} +``` + +We can look up the method handles for both these methods and wrap `problematicMethod` inside an `exceptionHandler`. +The resulting `MethodHandle` on invocation will handle the `IllegalArgumentException` properly, continuing to throw any other exceptions if they arise. + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +MethodHandle methodHandle = lookup.findStatic(Example.class, "problematicMethod", MethodType.methodType(int.class, String.class)); +MethodHandle handler = lookup.findStatic(Example.class, "exceptionHandler", +MethodType.methodType(int.class, IllegalArgumentException.class, String.class)); +MethodHandle wrapped = MethodHandles.catchException(methodHandle, IllegalArgumentException.class, handler); + +System.out.println(wrapped.invoke("valid")); // outputs "1" +System.out.println(wrapped.invoke("invalid")); // outputs "0" +``` + +### `finally` wrapper + +The `MethodHandles.tryFinally` method works similarly, but instead of an exception handler, it wraps an original method adding a try-finally block. + +Let's say we have a separate method `cleanupMethod` containing cleanup logic. The return type of this method must be the same as the original method's return type. +It must accept a `Throwable` followed by the resulting value coming from the original method, followed by all the arguments. + +```java +public static int cleanupMethod(IllegalArgumentException e, int result, String argument) { + System.out.println("inside finally block"); + return result; +} +``` + +We can wrap the method handle from previous example inside the try-finally block as follows: + +```java +MethodHandle cleanupMethod = lookup.findStatic(Example.class, "cleanupMethod", + MethodType.methodType(int.class, IllegalArgumentException.class, int.class, String.class)); + +MethodHandle wrappedWithFinally = MethodHandles.tryFinally(wrapped, cleanupMethod); + +System.out.println(wrappedWithFinally.invoke("valid")); // outputs "inside finally block 1" +System.out.println(wrappedWithFinally.invoke("invalid")); // outputs "inside finally block 0" +``` + +  +## Method handle transformations +As seen from previous examples, method handles can encapsulate more behavior than simply pointing to an underlying method. +We can obtain **adapter** method handles, which wrap original method handles to add certain behaviors such as argument reordering, pre-inserting, or filtering of the return values. + +Let's take a look at a couple of such transformations. + +### Type transformation +Method handle's type can be adapted to a new type using the `asType` method. If such type conversion is impossible, we will get a `WrongMethodTypeException`. +Remember, when we apply transformations, we actually have two method handles, where the original method handle is wrapped into some extra logic. +In this case, the wrapper will take in the arguments and try to convert them to match the original method handle's arguments. Once the original method handle +does its job and returns a result, the wrapper will attempt to cast this result to the given type. + +Assume we have a `test` method that accepts an `Object` and returns a `String`. We can adapt such a method to accept a more specific argument type, such as `String`: + +```java +MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", + MethodType.methodType(String.class, Object.class)); +MethodHandle adapter = originalMethodHandle.asType( + MethodType.methodType(String.class, String.class)); +String originalResult = (String) originalMethodHandle.invoke(111); // works +String adapterResult = (String) adapter.invoke("aaaaaa"); // works +adapterResult = (String) adapter.invoke(111); // fails +``` + +In fact, each time we use `invoke` on a `MethodHandle`, the first thing that happens is an `asType` call. +`invoke` accepts and returns `Object`s, which are then attempted to be converted to more specific types. +These specific types are derived from our code, i.e., the exact values that we pass as arguments and the type that we cast our return value to. +Once the types are successfully converted, the `invokeExact` method is then called for these specific types. + +### Permute arguments +To obtain an adapter method handle with reordered arguments, we can use `MethodHandles.permuteArguments`. + +For example, this is an original method: + +```java +public static void test(int v1, String v2, long v3, boolean v4) { + System.out.println(v1 + v2 + v3 + v4); +} +``` + +The original method handle will look like this: + +```java +MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", + MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); +``` + +The `permuteArguments` method accepts an original `MethodHandle` followed by the new `MethodType` for the adapter +method handle, followed by an index array designating the new order of the arguments: + +```java +MethodHandle reversedArguments = MethodHandles.permuteArguments(originalMethodHandle, + MethodType.methodType(void.class, boolean.class, long.class, String.class, int.class), 3, 2, 1, 0); + +reversedArguments.invoke(false, 1L, "str", 123); // outputs: "123str1false" +``` + +### Insert arguments +The `insertArguments` method provides a `MethodHandle` with one or more bound arguments. + +For example, let's look again at the original method handle from previous example: + +```java +MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", + MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); +``` + +We can easily obtain an adapter `MethodHandle` with `String` and `long` arguments bound in advance: + +```java +MethodHandle boundArguments = MethodHandles.insertArguments(originalMethodHandle, 1, "new", 3L); +``` + +To invoke the resulting adapter method handle, we only need to provide the arguments that are not pre-filled: + +```java +boundArguments.invoke(1, true); // outputs: "1new3true" +``` + +If we try to pass the arguments that are already prefilled, we will fail with `WrongMethodTypeException`. + +### Argument transformations + +We can use `MethodHandles.filterArguments` to apply transformations to the arguments before invocation of the original method handle. +To make it work, we have to provide: + +- the original method handle; +- the position of the first argument to transform; +- method handles for the transformations of each argument. + +If certain arguments don't require transformation, we can skip them by passing `null`. +It's also possible to skip the rest of the arguments entirely if we only need to transform a subset of them. + +Let's take a look at the following example. +Original method handle: + +```java +MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", + MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); +``` + +Let's create a method that transforms any `boolean` value by negating it: + +```java +private static boolean negate(boolean original) { + return !original; +} +``` + +Let's also create a method that increments any given integer value: + +```java +private static int increment(int original) { + return ++original; +} +``` + +Method handles for these transformation methods: + +```java +MethodHandle negate = lookup.findStatic(Adapters.class, "negate", MethodType.methodType(boolean.class, boolean.class)); +MethodHandle increment = lookup.findStatic(Adapters.class, "increment", MethodType.methodType(int.class, int.class)); +``` + +Now we can create a new method handle with transformations applied to two of the original arguments: + +```java +MethodHandle withFilters = MethodHandles.filterArguments(originalMethodHandle, 0, increment, null, null, negate); +withFilters.invoke(3, "abc", 5L, false); // outputs "4abc5true" +``` + +When we want to perform pre-processing of one or more arguments before the invocation of the original `MethodHandle`, +we can use `MethodHandles.foldArguments` and provide it with the method handle of any combiner method which will accept arguments starting at any preferred position. + +### Return value filtering +Similarly to arguments, we can use an adapter that will apply transformations to the return value. + +Let's imagine a situation where we have a method that returns a `String`, and we would like to channel any returned value from this method into another method +that replaces character `d` with `m` and brings the resulting value to the uppercase. + +Here's the original method handle for the `getSomeString` method which always returns the value `"dummy"`: + +```java +MethodHandle getSomeString = lookup.findStatic(Adapters.class, "getSomeString", MethodType.methodType(String.class)); +``` + +Here's the `resultTransform` method that performs transformations: + +```java +private static String resultTransform(String value) { + return value.replace('d', 'm').toUpperCase(); +} +``` + +Here is the method handle for our transformator method: + +```java +MethodHandle resultTransform = lookup.findStatic(Adapters.class, "resultTransform", MethodType.methodType(String.class, String.class)); +``` + +Finally, this is the combination of the two method handles where the result returned by `getSomeString` +method is then provided to the `resultTransform` method and modified accordingly: + +```java +MethodHandle getSomeUppercaseString = MethodHandles.filterReturnValue(getSomeString, resultTransform); +System.out.println(getSomeUppercaseString.invoke()); // outputs: "MUMMY" +``` + +  +## Conclusion +In this tutorial, we have looked into the Method Handles mechanism and learned how to efficiently use it. +We know now, that the method handles provide means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. + +The biggest differences are: +- Access checking happens only on method handle creation, which means that method handles should be passed around with caution. +- Method invocation is more performant with method handles due to different access checking approach. +- Method handles don't provide any tooling for listing the class members and inspecting their properties. +- Method handles make it easy to wrap the direct pointers to methods and fields into more complicated logic which includes argument and return value manipulations. \ No newline at end of file From 314646544199f91abc01185a2fe5d526729e927e Mon Sep 17 00:00:00 2001 From: Nataliia Dziubenko Date: Sat, 4 May 2024 22:52:29 +0200 Subject: [PATCH 06/26] Method Handles tutorial: fixes after review --- app/data/authors.yaml | 8 +- app/data/javadoc.json | 39 +- .../02_invoke/00_methodhandle.md | 473 +++++++++++------- 3 files changed, 327 insertions(+), 193 deletions(-) diff --git a/app/data/authors.yaml b/app/data/authors.yaml index 73fb847..96c500d 100644 --- a/app/data/authors.yaml +++ b/app/data/authors.yaml @@ -108,6 +108,10 @@ github: smthelusive twitter: worth_exploring photo_url: https://nataliiadziubenko.com/assets/images/nataliia.png - website: https://medium.com/@nataliiadziubenko + website: https://nataliiadziubenko.com/ description: | - Nataliia is a Software Engineer passionate about Java & JVM. \ No newline at end of file + Nataliia is a Senior Software Engineer at Xebia. She began her career in 2015 and has since worked with diverse + technologies and domains, primarily focusing on the JVM ecosystem. Nataliia is passionate and curious about everything + around JVM. She enjoys digging into the low-level concepts and diving into how things work under the hood. She also + loves sharing knowledge, so she speaks at conferences such as JFall, JSpring, Voxxed Days, and Java User Groups, + and writes a blog about JVM. \ No newline at end of file diff --git a/app/data/javadoc.json b/app/data/javadoc.json index c358cf4..d4a4f24 100644 --- a/app/data/javadoc.json +++ b/app/data/javadoc.json @@ -505,6 +505,7 @@ "String.equals(Object)": "java.base/java/lang/String.html#equals(java.lang.Object)", "String.regionMatches(boolean,int,String,int,int)": "java.base/java/lang/String.html#regionMatches(boolean,int,java.lang.String,int,int)", "String.join(CharSequence,CharSequence)": "java.base/java/lang/String.html#join(java.lang.CharSequence,java.lang.CharSequence)", + "String.valueOf(Object)": "java.base/java/lang/String.html#valueOf(java.lang.Object)", "StringIndexOutOfBoundsException": "java.base/java/lang/StringIndexOutOfBoundsException.html", @@ -1406,6 +1407,40 @@ "MessageDigest": "java.base/java/security/MessageDigest.html", "Security.getProviders()": "java.base/java/security/Security.html#getProviders()", "Security.addProvider(java.security.Provider)": "java.base/java/security/Security.html#addProvider(java.security.Provider)", - "Security.insertProviderAt": "java.base/java/security/Security.html#insertProviderAt(java.security.Provider,int)" - + "Security.insertProviderAt": "java.base/java/security/Security.html#insertProviderAt(java.security.Provider,int)", + + "Method": "java.base/java/lang/reflect/Method.html", + "Field": "java.base/java/lang/reflect/Field.html", + "Member": "java.base/java/lang/reflect/Member.html", + "Lookup": "java.base/java/lang/invoke/MethodHandles.Lookup.html", + "MethodType": "java.base/java/lang/invoke/MethodType.html", + "MethodHandles": "java.base/java/lang/invoke/MethodHandles.html", + "MethodHandles.Lookup.findVirtual(Class,String,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findVirtual(java.lang.Class,java.lang.String,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findStatic(Class,String,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStatic(java.lang.Class,java.lang.String,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findConstructor(Class,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findConstructor(java.lang.Class,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findGetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findGetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findSetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findSetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticGetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticGetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticSetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticSetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findVarHandle(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findVarHandle(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticVarHandle(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticVarHandle(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.unreflect(Method)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#unreflect(java.lang.reflect.Method)", + "MethodHandles.publicLookup()": "java.base/java/lang/invoke/MethodHandles.html#publicLookup()", + "MethodHandles.tryFinally(MethodHandle,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#tryFinally(java.lang.invoke.MethodHandle,java.lang.invoke.MethodHandle)", + "MethodHandles.permuteArguments(MethodHandle,MethodType,int...)": "java.base/java/lang/invoke/MethodHandles.html#permuteArguments(java.lang.invoke.MethodHandle,java.lang.invoke.MethodType,int...)", + "MethodHandles.insertArguments(MethodHandle,int,Object...)": "java.base/java/lang/invoke/MethodHandles.html#insertArguments(java.lang.invoke.MethodHandle,int,java.lang.Object...)", + "MethodHandles.filterArguments(MethodHandle,int,MethodHandle...)": "java.base/java/lang/invoke/MethodHandles.html#filterArguments(java.lang.invoke.MethodHandle,int,java.lang.invoke.MethodHandle...)", + "MethodHandles.foldArguments(MethodHandle,int,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#foldArguments(java.lang.invoke.MethodHandle,int,java.lang.invoke.MethodHandle)", + "MethodHandles.reflectAs(Class,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#reflectAs(java.lang.Class,java.lang.invoke.MethodHandle)", + "MethodHandles.arrayConstructor(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayConstructor(java.lang.Class)", + "MethodHandles.arrayElementSetter(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayElementSetter(java.lang.Class)", + "MethodHandles.arrayElementGetter(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayElementGetter(java.lang.Class)", + "MethodHandles.catchException(MethodHandle,Class,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#catchException(java.lang.invoke.MethodHandle,java.lang.Class,java.lang.invoke.MethodHandle)", + "MethodHandles.arrayLength(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayLength(java.lang.Class)", + "MethodHandle.invoke(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invoke(java.lang.Object...)", + "MethodHandle.invokeExact(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invokeExact(java.lang.Object...)", + "MethodHandle.invokeWithArguments(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invokeWithArguments(java.lang.Object...)", + "MethodHandle.asType(MethodType)": "java.base/java/lang/invoke/MethodHandle.html#asType(java.lang.invoke.MethodType)", + "VarHandle": "java.base/java/lang/invoke/VarHandle.html", + "WrongMethodTypeException": "java.base/java/lang/invoke/WrongMethodTypeException.html" } \ No newline at end of file diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index b6ed63a..12c1211 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -18,50 +18,54 @@ toc: - Method handle invocation {invocation} - Accessing fields {fields} - Working with arrays {arrays} - - Method handles vs Reflection {vsreflection} - - Conversion between Reflection API and method handles {unreflect} - Exception handling {exceptions} - Method handle transformations {transformations} + - Method Handles vs Reflection API {vsreflection} + - Conversion between Reflection API and method handles {unreflect} - Conclusion {conclusion} -last_update: 2024-04-29 +last_update: 2024-05-04 ---   ## What are Method Handles -Method Handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, but in fact, these two APIs serve different purposes. -There are some overlapping capabilities that both Reflection API and Method Handles can provide. -For example, method invocation, where Method Handles perform better. +Method Handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, +because both the Reflection API and Method Handles provide means to invoke methods and constructors and access fields. -What exactly is a Method Handle? It's a direct reference to a method, constructor, or field, which can be invoked. -Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the arguments, or transform the return values, for example. +What exactly is a Method Handle? It's a direct reference to a method, constructor, or field, which can be invoked. +The Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the +arguments, or transform the return values, for example. Let's take a closer look at what Method Handle mechanism can provide and how we can effectively use it.   ## Access checking -The access checking for method handle invocations is done differently compared to the Reflection API. -With reflection, each call results in access checks for the caller. -For method handles, the access is only checked when the method handle is created. -The access level that it has can't exceed the access level of the class where it's created; however, it can be further limited. +The access checking for method handle invocations is done differently compared to the Reflection API. With reflection, +each call results in access checks for the caller. For method handles, the access is only checked when the method handle +is created. -It is important to keep in mind, that if the method handle is created within a context that can access non-public members, when passed outside, it can still access non-public members. -As a result, non-public members can potentially be executed from code where they shouldn't be accessible. -It's a developer's responsibility to keep such method handles private to their context. +It is important to keep in mind, that if the method handle is created within a context where it can access non-public +members, when passed outside, it can still access those non-public members. As a result, non-public members can +potentially be accessed from code where they shouldn't be accessible. It's a developer's responsibility to keep such +method handles private to their context. Alternatively, the method handle can be created with access limitations right +away using the appropriate lookup object.   ## Method handle lookup -To create a method handle, we first need to create a `Lookup` object, which acts as a factory for creating method handles. -At this point, we can determine our level of trust in this lookup object based on our knowledge of how it will be used. -Will this lookup object or the method handles be passed somewhere else? +To create a method handle, we first need to create a [`Lookup`](javadoc:Lookup) object, which acts as a factory for +creating method handles. Depending on how the lookup object itself or the method handles are going to be used, we can +decide whether we should limit its access level. -If we don't fully trust our lookup object, we can create it using `MethodHandles::publicLookup` method, so it will only be able to -search for public members in public classes within packages that are exported unconditionally: +For example, if we create a method handle pointing to a private method and that method handle is accessible from outside, +so is the private method. Normally we would like to avoid that. One way is to make the lookup object and method handle +`private` too. Another option is to create the lookup object using [`MethodHandles.publicLookup`](javadoc:MethodHandles.publicLookup()) +method, so it will only be able to search for public members in public classes within packages that are exported unconditionally: ```java MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); ``` -It's possible to create a lookup object that can create method handles for any members, including private and protected ones: +If we are going to keep the lookup object and the method handles private, it's safe to give them access to any members, +including private and protected ones: ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); @@ -69,20 +73,23 @@ MethodHandles.Lookup lookup = MethodHandles.lookup();   ## Method type -To be able to look up a method handle, we also need to provide the type information of the method or field. -The method type information is represented as `MethodType` object. -To instantiate a `MethodType`, we have to provide the return type as the first parameter followed by all the argument types: +To be able to look up a method handle, we also need to provide the type information of the method or field. The method +type information is represented as [`MethodType`](javadoc:MethodType) object. To instantiate a `MethodType`, +we have to provide the return type as the first parameter followed by all the argument types: ```java -MethodType methodType = MethodType.methodType(int.class /* the method returns integer */, +MethodType methodType = MethodType.methodType(int.class /* the method returns integer */, String.class /* and accepts a single String argument*/); ``` -Having the `Lookup` and the `MethodType` instances, we can look up the method handle. -For virtual methods, we should use `findVirtual`, and for static methods `findStatic`. -Both these methods accept the following arguments: a `Class`, where the method is located, a method name represented as a `String`, and a `MethodType` instance. +Having the `Lookup` and the `MethodType` instances, we can look up the method handle. For instance methods, we should +use [`Lookup.findVirtual`](javadoc:MethodHandles.Lookup.findVirtual(Class,String,MethodType)), and for static methods +[`Lookup.findStatic`](javadoc:MethodHandles.Lookup.findStatic(Class,String,MethodType)). Both these methods accept the +following arguments: a `Class`, where the method is located, a method name represented as a `String`, and a `MethodType` +instance. -In the example below, we are looking up a virtual method `String::replace`, which accepts two `char` arguments and returns a `String`: +In the example below, we are looking up an instance method [`String.replace`](javadoc:String.replace(char,char)), which +accepts two `char` arguments and returns a `String`: ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); @@ -90,25 +97,29 @@ MethodType replaceMethodType = MethodType.methodType(String.class, char.class, c MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); ``` -In the next example, we are looking up a static method `String::valueOf`, which accepts an `Object` and returns a `String`: +In the next example, we are looking up a static method [`String.valueOf`](javadoc:String.valueOf(Object)), which accepts +an `Object` and returns a `String`: ```java MethodType valueOfMethodType = MethodType.methodType(String.class, Object.class); MethodHandle valueOfMethodHandle = lookup.findStatic(String.class, "valueOf", valueOfMethodType); ``` -Similarly, we could use `findConstructor` method to look up a method handle pointing to any constructor. +Similarly, we could use [`Lookup.findConstructor`](javadoc:MethodHandles.Lookup.findConstructor(Class,MethodType)) +method to look up a method handle pointing to any constructor. Finally, when we have obtained a method handle, we can invoke the underlying method.   ## Method handle invocation -The invocation can also be done in multiple ways. +The invocation can also be done in multiple ways. -All the methods that facilitate invocation eventually funnel down to a single method that is called in the end: `MethodHandle::invokeExact`. -As the method name suggests, the arguments provided to `invokeExact` method must strictly match the method handle's type. +All the methods that facilitate invocation eventually funnel down to a single method that is called in the end: +[`MethodHandle.invokeExact`](javadoc:MethodHandle.invokeExact(Object...)). As the method name suggests, the arguments +provided to `invokeExact` method must strictly match the method handle's type. -For example, if we invoke a `String::replace` method, the arguments must strictly correspond to a `String` return type and two `char` arguments: +For example, if we invoke a `String.replace` method, the arguments must strictly +correspond to a `String` return type and two `char` arguments: ```java MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); @@ -116,8 +127,9 @@ MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", r String result = (String) replaceMethodHandle.invokeExact("dummy", 'd', 'm'); ``` -`MethodHandle.invoke` is more permissive. It attempts to obtain a new method handle with adjusted types that would strictly match the types of provided arguments. -After that, it will be able to invoke the adjusted method handle using `invokeExact`. +[`MethodHandle.invoke`](javadoc:MethodHandle.invoke(Object...)) is more permissive. It attempts to obtain a new method +handle with adjusted types that would strictly match the types of provided arguments. After that, it will be able to +invoke the adjusted method handle using `invokeExact`. ```java MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); @@ -125,17 +137,21 @@ MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", r String result = (String) replaceMethodHandle.invoke((Object)"dummy", (Object)'d', (Object)'m'); // would fail with `invokeExact` ``` -One other alternative to invoke a method handle is to use `MethodHandle::invokeWithArguments`. -The result of this method invocation is equivalent to `invoke`, with the only difference that all the arguments can be provided as an array or list of objects. +One other alternative to invoke a method handle is to use [`MethodHandle.invokeWithArguments`](javadoc:MethodHandle.invokeWithArguments(Object...)). +The result of this method invocation is equivalent to `invoke`, with the only difference that all the arguments can be +provided as an array or list of objects. -One interesting feature of this method is that if the number of provided arguments exceeds the expected number, -all the leftover arguments will be squashed into the last argument, which will be treated as an array. +One interesting feature of this method is that if the number of provided arguments exceeds the expected number, all the +leftover arguments will be squashed into the last argument, which will be treated as an array.   ## Accessing fields -It is possible to create method handles that have read or write access to fields. -For instance fields, this is facilitated by `findGetter` and `findSetter` methods, and for static fields, by `findStaticGetter` and `findStaticSetter` methods. -We don't need to provide a `MethodType` instance; instead, we should provide a single type, which is the type of the field. +It is possible to create method handles that have read or write access to fields. For instance fields, this is +facilitated by [`findGetter`](javadoc:MethodHandles.Lookup.findGetter(Class,String,Class)) and +[`findSetter`](javadoc:MethodHandles.Lookup.findSetter(Class,String,Class)) methods, and for static fields, by +[`findStaticGetter`](javadoc:MethodHandles.Lookup.findStaticGetter(Class,String,Class)) and +[`findStaticSetter`](javadoc:MethodHandles.Lookup.findStaticSetter(Class,String,Class)) methods. We don't need to provide +a `MethodType` instance; instead, we should provide a single type, which is the type of the field. For example, if we have a static field `magic` in our `Example` class: @@ -143,7 +159,7 @@ For example, if we have a static field `magic` in our `Example` class: private static String magic = "initial value static field"; ``` -Given that we have created a `Lookup` object +Given that we have created a `Lookup` object: ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); @@ -172,7 +188,8 @@ MethodHandle setterMethodHandle = lookup.findSetter(Example.class, "abc", String MethodHandle getterMethodHandle = lookup.findGetter(Example.class, "abc", String.class); ``` -To use setter and getter method handles with an instance field, we must first obtain an instance of the class where the field belongs: +To use setter and getter method handles with an instance field, we must first obtain an instance of the class where the +field belongs: ```java Example example = new Example(); @@ -185,116 +202,65 @@ setterMethodHandle.invoke(example, "new value"); String result = (String) getterMethodHandle.invoke(example); // result == `new value` ``` -Although it is possible to read and write field values using method handles, it's not common practice. -For fields, it's more suitable to use `VarHandle`s instead, which can be created using `findVarHandle` and `findStaticVarHandle` methods. +Although it is possible to read and write field values using method handles, it's not common practice. For fields, +it's more suitable to use [`VarHandle`](javadoc:VarHandle)s instead, which can be created using +[`findVarHandle`](javadoc:MethodHandles.Lookup.findVarHandle(Class,String,Class)) and +[`findStaticVarHandle`](javadoc:MethodHandles.Lookup.findStaticVarHandle(Class,String,Class)) +methods.   ## Working with arrays -The `MethodHandles` class contains certain methods that provide a number of preset method handles. -These include method handles that allow array manipulations. Creating these method handles doesn't require access checking, so the lookup object is not necessary. +The [`MethodHandles`](javadoc:MethodHandles) class contains methods that provide a number of preset method handles. +These include method handles that allow array manipulations. Creating these method handles doesn't require access checking, +so the lookup object is not necessary. -Let's create an array of Strings containing 5 elements using `arrayConstructor`: +Let's create an array of Strings containing 5 elements using [`arrayConstructor`](javadoc:MethodHandles.arrayConstructor(Class)): ```java MethodHandle arrayConstructor = MethodHandles.arrayConstructor(String[].class); String[] arr = (String[]) arrayConstructor.invoke(5); ``` -To modify a single element, we can use `arrayElementSetter`, to which we provide the reference to the target array, the index of an element, and the new value: +To modify a single element, we can use [`arrayElementSetter`](javadoc:MethodHandles.arrayElementSetter(Class)), to which +we provide the reference to the target array, the index of an element, and the new value: ```java MethodHandle elementSetter = MethodHandles.arrayElementSetter(String[].class); elementSetter.invoke(arr, 4, "test"); ``` -To read the value of a single element, we should use `arrayElementGetter` method handle, to which we provide the reference to an array and the element index: +To read the value of a single element, we should use [`arrayElementGetter`](javadoc:MethodHandles.arrayElementGetter(Class)) +method handle, to which we provide the reference to an array and the element index: ```java MethodHandle elementGetter = MethodHandles.arrayElementGetter(String[].class); String element = (String) elementGetter.invoke(arr, 4); // element == "test" ``` -We could also use the method handle provided by `arrayLength` to get the array length as integer: +We could also use the method handle provided by [`arrayLength`](javadoc:MethodHandles.arrayLength(Class)) to get the array +length as integer: ```java MethodHandle arrayLength = MethodHandles.arrayLength(String[].class); int length = (int) arrayLength.invoke(arr); // length == 5 ``` -  -## Method handles vs Reflection -The Method Handles mechanism was not designed for wide usage. Originally, it was meant as a tool to assist compiler and language runtime developers. -It has a narrower range of capabilities than the Reflection API. -For example, the Reflection API allows listing the class members and inspecting their properties, which cannot be done using method handles. - -There are also certain differences when it comes to access checking and security considerations. -The Core Reflection API performs access checking against every caller, on every call, while for method handles, access is checked only during construction. -This makes invocation through method handles faster than through reflection. -However, certain precautions have to be taken so the method handle is not passed to the code where it shouldn't be accessible. - -Method handles and reflection serve different purposes, so it's important to pick the right tool based on your needs. - -  -## Conversion between Reflection API and method handles -The `Lookup` object can be used to convert Reflection API objects to behaviorally equivalent method handles, which provide more direct and efficient access to the underlying class members. - -To create a method handle pointing to a given `Method` (given that the lookup class has permission to do so), we can use `unreflect`. - -Let's say we have a `test` method in our `Example` class which accepts a `String` argument and returns a `String`. -Using the Reflection API, we can obtain a `Method` object: - -```java -Class clazz = Class.forName("Example"); -Method method = clazz.getMethod("test", String.class); -``` - -With the help of the lookup object, we can `unreflect` the `Method` object to obtain a `MethodHandle`: - -```java -MethodHandles.Lookup lookup = MethodHandles.lookup(); -MethodHandle methodHandle = lookup.unreflect(method); -String result = (String) methodHandle.invoke("something"); -``` - -Similarly, given a `Field` object, we can obtain getter and setter method handles: - -```java -Field field = clazz.getField("magic"); -MethodHandle setterMethodHandle = lookup.unreflectSetter(field); -MethodHandle getterMethodHandle = lookup.unreflectGetter(field); -setterMethodHandle.invoke("something"); -String result = (String) getterMethodHandle.invoke(); // result == "something" -``` - -Conversion from `MethodHandle` to a `Member` is also possible, with the condition that no transformations have been performed to the given `MethodHandle`. - -Let's say we have a method handle pointing directly to a method. We can use the `MethodHandles::reflectAs` method to obtain the `Method` object: - -```java -Method method = MethodHandles.reflectAs(Method.class, methodHandle); -``` - -It works similarly for the `Field` object: - -```java -Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same result is achieved by reflecting `setterMethodHandle` -``` -   ## Exception handling +Both `invokeExact` and `invoke` throw [`Throwable`](javadoc:Throwable), so there is no limitation to what an underlying +method can throw. The method that invokes a method handle must either explicitly throw a `Throwable` or catch it. -Both `invokeExact` and `invoke` throw `Throwable`, so there is no limitation to what an underlying method can throw. -The method that invokes a method handle must either explicitly throw a `Throwable` or catch it. - -There are certain methods in the `MethodHandles` API that can make exception handling easier. -Let's take a look at several examples. +There are certain methods in the `MethodHandles` API that can make exception handling easier. Let's take a look at +several examples. ### `catch` wrapper -`MethodHandles.catchException` method can wrap a given method handle inside a provided exception handler method handle. +The [`MethodHandles.catchException`](javadoc:MethodHandles.catchException(MethodHandle,Class,MethodHandle)) method can +wrap a given method handle inside a provided exception handler method handle. -Say, we have a method `problematicMethod` that does some job, and a method `exceptionHandler` that handles a particular exception `IllegalArgumentException`. -The exception handler method must return the same type as the original method. -The first argument it accepts is a `Throwable` that we're interested in, after which follow the rest of the arguments that we've originally accepted: +Say, we have a method `problematicMethod` that does some job, and a method `exceptionHandler` that handles a particular +exception [`IllegalArgumentException`](javadoc:IllegalArgumentException). The exception handler method must return the +same type as the original method. The first argument it accepts is a `Throwable` that we're interested in, after which +follow the rest of the arguments that we've originally accepted: ```java public static int problematicMethod(String argument) throws IllegalArgumentException { @@ -310,8 +276,9 @@ public static int exceptionHandler(IllegalArgumentException e, String argument) } ``` -We can look up the method handles for both these methods and wrap `problematicMethod` inside an `exceptionHandler`. -The resulting `MethodHandle` on invocation will handle the `IllegalArgumentException` properly, continuing to throw any other exceptions if they arise. +We can look up the method handles for both these methods and wrap `problematicMethod` inside an `exceptionHandler`. The +resulting `MethodHandle` will handle the `IllegalArgumentException` properly on invocation, continuing to throw any +other exceptions if they arise. ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); @@ -325,14 +292,15 @@ System.out.println(wrapped.invoke("invalid")); // outputs "0" ``` ### `finally` wrapper +The [`MethodHandles.tryFinally`](javadoc:MethodHandles.tryFinally(MethodHandle,MethodHandle)) method works similarly, +but instead of an exception handler, it wraps a target method adding a try-finally block. -The `MethodHandles.tryFinally` method works similarly, but instead of an exception handler, it wraps an original method adding a try-finally block. - -Let's say we have a separate method `cleanupMethod` containing cleanup logic. The return type of this method must be the same as the original method's return type. -It must accept a `Throwable` followed by the resulting value coming from the original method, followed by all the arguments. +Let's say we have a separate method `cleanupMethod` containing cleanup logic. The return type of this method must be the +same as the target method's return type. It must accept a `Throwable` followed by the resulting value coming from the +target method, followed by all the arguments. ```java -public static int cleanupMethod(IllegalArgumentException e, int result, String argument) { +public static int cleanupMethod(Throwable e, int result, String argument) { System.out.println("inside finally block"); return result; } @@ -342,48 +310,53 @@ We can wrap the method handle from previous example inside the try-finally block ```java MethodHandle cleanupMethod = lookup.findStatic(Example.class, "cleanupMethod", - MethodType.methodType(int.class, IllegalArgumentException.class, int.class, String.class)); + MethodType.methodType(int.class, Throwable.class, int.class, String.class)); -MethodHandle wrappedWithFinally = MethodHandles.tryFinally(wrapped, cleanupMethod); +MethodHandle wrappedWithFinally = MethodHandles.tryFinally(methodHandle, cleanupMethod); -System.out.println(wrappedWithFinally.invoke("valid")); // outputs "inside finally block 1" -System.out.println(wrappedWithFinally.invoke("invalid")); // outputs "inside finally block 0" +System.out.println(wrappedWithFinally.invoke("valid")); // outputs "inside finally block" and "1" +System.out.println(wrappedWithFinally.invoke("invalid")); // outputs "inside finally block" and throws java.lang.IllegalArgumentException ```   ## Method handle transformations -As seen from previous examples, method handles can encapsulate more behavior than simply pointing to an underlying method. -We can obtain **adapter** method handles, which wrap original method handles to add certain behaviors such as argument reordering, pre-inserting, or filtering of the return values. +As seen from previous examples, method handles can encapsulate more behavior than simply pointing to an underlying +method. We can obtain **adapter** method handles, which wrap target method handles to add certain behaviors such as +argument reordering, pre-inserting, or filtering of the return values. Let's take a look at a couple of such transformations. ### Type transformation -Method handle's type can be adapted to a new type using the `asType` method. If such type conversion is impossible, we will get a `WrongMethodTypeException`. -Remember, when we apply transformations, we actually have two method handles, where the original method handle is wrapped into some extra logic. -In this case, the wrapper will take in the arguments and try to convert them to match the original method handle's arguments. Once the original method handle -does its job and returns a result, the wrapper will attempt to cast this result to the given type. +A method handle's type can be adapted to a new type using the [`asType`](javadoc:MethodHandle.asType(MethodType)) method. +If such type conversion is impossible, we will get a [`WrongMethodTypeException`](javadoc:WrongMethodTypeException). +Remember, when we apply transformations, we actually have two method handles, where the original method handle is wrapped +into some extra logic. In this case, the wrapper will take in the arguments and try to convert them to match the original +method handle's arguments. Once the original method handle does its job and returns a result, the wrapper will attempt to +cast this result to the given type. -Assume we have a `test` method that accepts an `Object` and returns a `String`. We can adapt such a method to accept a more specific argument type, such as `String`: +Assume we have a `test` method that accepts an `Object` and returns a `String`. We can adapt such a method to accept a +more specific argument type, such as `String`: ```java -MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", +MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", MethodType.methodType(String.class, Object.class)); -MethodHandle adapter = originalMethodHandle.asType( +MethodHandle adapter = targetMethodHandle.asType( MethodType.methodType(String.class, String.class)); -String originalResult = (String) originalMethodHandle.invoke(111); // works +String originalResult = (String) targetMethodHandle.invoke(111); // works String adapterResult = (String) adapter.invoke("aaaaaa"); // works adapterResult = (String) adapter.invoke(111); // fails ``` -In fact, each time we use `invoke` on a `MethodHandle`, the first thing that happens is an `asType` call. -`invoke` accepts and returns `Object`s, which are then attempted to be converted to more specific types. -These specific types are derived from our code, i.e., the exact values that we pass as arguments and the type that we cast our return value to. +In fact, each time we use `invoke` on a `MethodHandle`, the first thing that happens is an `asType` call. `invoke` +accepts and returns `Object`s, which are then attempted to be converted to more specific types. These specific types are +derived from our code, i.e., the exact values that we pass as arguments and the type that we cast our return value to. Once the types are successfully converted, the `invokeExact` method is then called for these specific types. ### Permute arguments -To obtain an adapter method handle with reordered arguments, we can use `MethodHandles.permuteArguments`. +To obtain an adapter method handle with reordered arguments, we can use +[`MethodHandles.permuteArguments`](javadoc:MethodHandles.permuteArguments(MethodHandle,MethodType,int...)). -For example, this is an original method: +For example, let's create a `test` method that accepts a bunch of arguments of different types: ```java public static void test(int v1, String v2, long v3, boolean v4) { @@ -391,37 +364,39 @@ public static void test(int v1, String v2, long v3, boolean v4) { } ``` -The original method handle will look like this: +And look up a method handle for it: ```java -MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", +MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); ``` -The `permuteArguments` method accepts an original `MethodHandle` followed by the new `MethodType` for the adapter -method handle, followed by an index array designating the new order of the arguments: +The `permuteArguments` method accepts: +- Target method handle, in our case the one pointing to `test` method; +- New `MethodType` with all the arguments reordered in desired way; +- An index array designating the new order of the arguments. ```java -MethodHandle reversedArguments = MethodHandles.permuteArguments(originalMethodHandle, +MethodHandle reversedArguments = MethodHandles.permuteArguments(targetMethodHandle, MethodType.methodType(void.class, boolean.class, long.class, String.class, int.class), 3, 2, 1, 0); - reversedArguments.invoke(false, 1L, "str", 123); // outputs: "123str1false" ``` ### Insert arguments -The `insertArguments` method provides a `MethodHandle` with one or more bound arguments. +The [`MethodHandles.insertArguments`](javadoc:MethodHandles.insertArguments(MethodHandle,int,Object...)) method provides +a `MethodHandle` with one or more bound arguments. -For example, let's look again at the original method handle from previous example: +For example, let's look again at the method handle from previous example: ```java -MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", +MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); ``` We can easily obtain an adapter `MethodHandle` with `String` and `long` arguments bound in advance: ```java -MethodHandle boundArguments = MethodHandles.insertArguments(originalMethodHandle, 1, "new", 3L); +MethodHandle boundArguments = MethodHandles.insertArguments(targetMethodHandle, 1, "new", 3L); ``` To invoke the resulting adapter method handle, we only need to provide the arguments that are not pre-filled: @@ -430,25 +405,23 @@ To invoke the resulting adapter method handle, we only need to provide the argum boundArguments.invoke(1, true); // outputs: "1new3true" ``` -If we try to pass the arguments that are already prefilled, we will fail with `WrongMethodTypeException`. +If we try to pass the arguments that are already prefilled, we will fail with a `WrongMethodTypeException`. -### Argument transformations +### Filter arguments +We can use [`MethodHandles.filterArguments`](javadoc:MethodHandles.filterArguments(MethodHandle,int,MethodHandle...)) +to apply transformations to the arguments before invocation of the target method handle. To make it work, we have to provide: -We can use `MethodHandles.filterArguments` to apply transformations to the arguments before invocation of the original method handle. -To make it work, we have to provide: +- The target method handle; +- The position of the first argument to transform; +- Method handles for the transformations of each argument. -- the original method handle; -- the position of the first argument to transform; -- method handles for the transformations of each argument. +If certain arguments don't require transformation, we can skip them by passing `null`. It's also possible to skip the +rest of the arguments entirely if we only need to transform a subset of them. -If certain arguments don't require transformation, we can skip them by passing `null`. -It's also possible to skip the rest of the arguments entirely if we only need to transform a subset of them. - -Let's take a look at the following example. -Original method handle: +Let's take a look at the following example. ```java -MethodHandle originalMethodHandle = lookup.findStatic(Adapters.class, "test", +MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); ``` @@ -471,30 +444,85 @@ private static int increment(int original) { Method handles for these transformation methods: ```java -MethodHandle negate = lookup.findStatic(Adapters.class, "negate", MethodType.methodType(boolean.class, boolean.class)); -MethodHandle increment = lookup.findStatic(Adapters.class, "increment", MethodType.methodType(int.class, int.class)); +MethodHandle negate = lookup.findStatic(Example.class, "negate", MethodType.methodType(boolean.class, boolean.class)); +MethodHandle increment = lookup.findStatic(Example.class, "increment", MethodType.methodType(int.class, int.class)); ``` Now we can create a new method handle with transformations applied to two of the original arguments: ```java -MethodHandle withFilters = MethodHandles.filterArguments(originalMethodHandle, 0, increment, null, null, negate); +MethodHandle withFilters = MethodHandles.filterArguments(targetMethodHandle, 0, increment, null, null, negate); withFilters.invoke(3, "abc", 5L, false); // outputs "4abc5true" ``` -When we want to perform pre-processing of one or more arguments before the invocation of the original `MethodHandle`, -we can use `MethodHandles.foldArguments` and provide it with the method handle of any combiner method which will accept arguments starting at any preferred position. +### Fold arguments +When we want to perform pre-processing of one or more arguments before the invocation of a `MethodHandle`, we +can use [`MethodHandles.foldArguments`](javadoc:MethodHandles.foldArguments(MethodHandle,int,MethodHandle)) and provide +it with the method handle of any combiner method which will accept arguments starting at any preferred position. + +Let's assume that we have a `target` method: -### Return value filtering +```java +private static void target(int ignored, int sum, int a, int b) { + System.out.printf("%s + %s equals %s and %s is ignored%n", a, b, sum, ignored); +} +``` + +Using `foldArguments` we can pre-process subset of its arguments and insert the resulting value as another +argument and proceed to execution of the `target` method. + +In our example, we have arguments `int a, int b` at the end. We can pre-process any amount of arguments, but they all +must be at the end. Let's say, we would like to calculate a sum of these two values `a` and `b`, so let's create a method +for that: + +```java +private static int sum(int a, int b) { + return a + b; +} +``` + +Where will the resulting value go exactly? It will be inserted into one of the arguments of our `target` method. It must +be the argument right before the arguments that we are going to fold, so in our example argument `int sum`. The argument +reserved for the folding result can't be at any other position. If the `target` method needs to accept more arguments not +related to this folding logic, they all must go first. + +Let's create the method handles and see how we should combine them together: + +```java +MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "target", + MethodType.methodType(void.class, int.class, int.class, int.class, int.class)); +MethodHandle combinerMethodHandle = lookup.findStatic(Example.class, "sum", + MethodType.methodType(int.class, int.class, int.class)); +MethodHandle preProcessedArguments = MethodHandles.foldArguments(targetMethodHandle, 1, combinerMethodHandle); +``` + +The `foldArguments` method accepts: +- Target method handle, in our case the one pointing to `target` method; +- `int` number specifying the starting position of arguments related to folding. In our case, the `sum` argument + is located at position `1`, so we passed `1`. If we skip this argument, `pos` will default to `0`. +- Combiner method handle, in our case it's the one pointing to `sum` method. + +At the end, we can invoke the resulting method handle and pass all the arguments except `sum` which is going to be +pre-calculated: + +```java +preProcessedArguments.invokeExact(10000, 1, 2); // outputs: "1 + 2 equals 3 and 10000 is ignored" +``` + +It is possible that the combiner method processes values but doesn't return anything. In this case, there is no need +for a result placeholder in `target` method argument list. + +### Filter return value Similarly to arguments, we can use an adapter that will apply transformations to the return value. -Let's imagine a situation where we have a method that returns a `String`, and we would like to channel any returned value from this method into another method -that replaces character `d` with `m` and brings the resulting value to the uppercase. +Let's imagine a situation where we have a method that returns a `String`, and we would like to channel any returned +value from this method into another method that replaces character `d` with `m` and brings the resulting value to the +uppercase. -Here's the original method handle for the `getSomeString` method which always returns the value `"dummy"`: +Here's the method handle for the `getSomeString` method which always returns the value `"dummy"`: ```java -MethodHandle getSomeString = lookup.findStatic(Adapters.class, "getSomeString", MethodType.methodType(String.class)); +MethodHandle getSomeString = lookup.findStatic(Example.class, "getSomeString", MethodType.methodType(String.class)); ``` Here's the `resultTransform` method that performs transformations: @@ -508,24 +536,91 @@ private static String resultTransform(String value) { Here is the method handle for our transformator method: ```java -MethodHandle resultTransform = lookup.findStatic(Adapters.class, "resultTransform", MethodType.methodType(String.class, String.class)); +MethodHandle resultTransform = lookup.findStatic(Example.class, "resultTransform", MethodType.methodType(String.class, String.class)); ``` -Finally, this is the combination of the two method handles where the result returned by `getSomeString` -method is then provided to the `resultTransform` method and modified accordingly: +Finally, this is the combination of the two method handles where the result returned by the `getSomeString` method is +then provided to the `resultTransform` method and modified accordingly: ```java MethodHandle getSomeUppercaseString = MethodHandles.filterReturnValue(getSomeString, resultTransform); System.out.println(getSomeUppercaseString.invoke()); // outputs: "MUMMY" ``` +  +## Method Handles vs Reflection API +Method Handles were introduced in [JDK7](https://docs.oracle.com/javase/7/docs/index.html) as a tool to assist +compiler and language runtime developers. They were never meant to replace reflection. The Reflection API offers something +that Method Handles cannot, which is listing the class members and inspecting their properties. Method Handles, on the +other hand, can be transformed and manipulated in a way that is not possible with Reflection API. + +When it comes to method invocation, there are differences related to access checking and security considerations. The +Reflection API performs access checking against every caller, on every call, while for method handles, access is +checked only during construction. This makes invocation through method handles faster than through reflection. +However, certain precautions have to be taken so the method handle is not passed to the code where it shouldn't be +accessible. + +  +## Conversion between Reflection API and method handles +The `Lookup` object can be used to convert Reflection API objects to behaviorally equivalent method handles, which +provide more direct and efficient access to the underlying class members. + +To create a method handle pointing to a given [`Method`](javadoc:Method) (given that the lookup class has permission to do so), we can +use `unreflect`. + +Let's say we have a `test` method in our `Example` class which accepts a `String` argument and returns a `String`. Using +the Reflection API, we can obtain a `Method` object: + +```java +Method method = Example.class.getMethod("test", String.class); +``` + +With the help of the lookup object, we can [`unreflect`](javadoc:MethodHandles.Lookup.unreflect(Method)) the `Method` +object to obtain a `MethodHandle`: + +```java +MethodHandles.Lookup lookup = MethodHandles.lookup(); +MethodHandle methodHandle = lookup.unreflect(method); +String result = (String) methodHandle.invoke("something"); +``` + +Similarly, given a [`Field`](javadoc:Field) object, we can obtain getter and setter method handles: + +```java +Field field = Example.class.getField("magic"); +MethodHandle setterMethodHandle = lookup.unreflectSetter(field); +MethodHandle getterMethodHandle = lookup.unreflectGetter(field); +setterMethodHandle.invoke("something"); +String result = (String) getterMethodHandle.invoke(); // result == "something" +``` + +Conversion from `MethodHandle` to a [`Member`](javadoc:Member) is also possible, with the condition that no transformations +have been performed to the given `MethodHandle`. + +Let's say we have a method handle pointing directly to a method. We can use the +[`MethodHandles.reflectAs`](javadoc:MethodHandles.reflectAs(Class,MethodHandle)) method to obtain the `Method` object: + +```java +Method method = MethodHandles.reflectAs(Method.class, methodHandle); +``` + +It works similarly for the `Field` object: + +```java +Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same result is achieved by reflecting `setterMethodHandle` +``` +   ## Conclusion -In this tutorial, we have looked into the Method Handles mechanism and learned how to efficiently use it. -We know now, that the method handles provide means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. +In this tutorial, we have looked into the Method Handle mechanism and learned how to efficiently use it. We now know, +that method handles provide means for efficient method invocation, but this mechanism is not meant to replace the +Reflection API. The biggest differences are: -- Access checking happens only on method handle creation, which means that method handles should be passed around with caution. -- Method invocation is more performant with method handles due to different access checking approach. + +- Access checking happens only on method handle creation, which means that method handles should be passed around with + caution. +- Method invocation is more performant with method handles due to a different access checking approach. - Method handles don't provide any tooling for listing the class members and inspecting their properties. -- Method handles make it easy to wrap the direct pointers to methods and fields into more complicated logic which includes argument and return value manipulations. \ No newline at end of file +- Method handles make it easy to wrap the direct pointers to methods and fields into more complicated logic which + includes argument and return value manipulations. \ No newline at end of file From ee2aad8414b38339dde7f6228c7f9f7eaffbb2c6 Mon Sep 17 00:00:00 2001 From: Nataliia Dziubenko Date: Tue, 21 May 2024 12:19:59 +0200 Subject: [PATCH 07/26] Method handle tutorial review fixes --- .../02_invoke/00_methodhandle.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 12c1211..1d5b3d7 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -8,10 +8,10 @@ category_order: 1 layout: learn/tutorial.html subheader_select: tutorials main_css_id: learn -description: "What is Method Handle mechanism, how is it different from Reflection API, and what tooling does it provide." +description: "What is method handle mechanism, how is it different from Reflection API, and what tooling does it provide." author: ["NataliiaDziubenko"] toc: - - What are Method Handles {intro} + - What are method handles {intro} - Access checking {access} - Method handle lookup {lookup} - Method type {methodtype} @@ -27,15 +27,15 @@ last_update: 2024-05-04 ---   -## What are Method Handles -Method Handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, -because both the Reflection API and Method Handles provide means to invoke methods and constructors and access fields. +## What are method handles +Method handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, +because both the Reflection API and method handles provide means to invoke methods and constructors and access fields. -What exactly is a Method Handle? It's a direct reference to a method, constructor, or field, which can be invoked. +What exactly is a method handle? It's a direct reference to a method, constructor, or field, which can be invoked. The Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the arguments, or transform the return values, for example. -Let's take a closer look at what Method Handle mechanism can provide and how we can effectively use it. +Let's take a closer look at what method handle mechanism can provide and how we can effectively use it.   ## Access checking @@ -88,8 +88,8 @@ use [`Lookup.findVirtual`](javadoc:MethodHandles.Lookup.findVirtual(Class,String following arguments: a `Class`, where the method is located, a method name represented as a `String`, and a `MethodType` instance. -In the example below, we are looking up an instance method [`String.replace`](javadoc:String.replace(char,char)), which -accepts two `char` arguments and returns a `String`: +In the example below, we are using `Lookup.findVirtual` method to look up an instance method +[`String.replace`](javadoc:String.replace(char,char)), which accepts two `char` arguments and returns a `String`: ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); @@ -97,8 +97,8 @@ MethodType replaceMethodType = MethodType.methodType(String.class, char.class, c MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); ``` -In the next example, we are looking up a static method [`String.valueOf`](javadoc:String.valueOf(Object)), which accepts -an `Object` and returns a `String`: +In the next example, we are using `Lookup.findStatic` to look up a static method +[`String.valueOf`](javadoc:String.valueOf(Object)), which accepts an `Object` and returns a `String`: ```java MethodType valueOfMethodType = MethodType.methodType(String.class, Object.class); @@ -257,10 +257,10 @@ several examples. The [`MethodHandles.catchException`](javadoc:MethodHandles.catchException(MethodHandle,Class,MethodHandle)) method can wrap a given method handle inside a provided exception handler method handle. -Say, we have a method `problematicMethod` that does some job, and a method `exceptionHandler` that handles a particular -exception [`IllegalArgumentException`](javadoc:IllegalArgumentException). The exception handler method must return the -same type as the original method. The first argument it accepts is a `Throwable` that we're interested in, after which -follow the rest of the arguments that we've originally accepted: +Say, we have a method `problematicMethod` that performs some business logic, and a method `exceptionHandler` that handles +a particular exception [`IllegalArgumentException`](javadoc:IllegalArgumentException). The exception handler method must +return the same type as the original method. The first argument it accepts is a `Throwable` that we're interested in, +after which follow the rest of the arguments that we've originally accepted: ```java public static int problematicMethod(String argument) throws IllegalArgumentException { @@ -549,9 +549,9 @@ System.out.println(getSomeUppercaseString.invoke()); // outputs: "MUMMY"   ## Method Handles vs Reflection API -Method Handles were introduced in [JDK7](https://docs.oracle.com/javase/7/docs/index.html) as a tool to assist +Method handles were introduced in [JDK7](https://docs.oracle.com/javase/7/docs/index.html) as a tool to assist compiler and language runtime developers. They were never meant to replace reflection. The Reflection API offers something -that Method Handles cannot, which is listing the class members and inspecting their properties. Method Handles, on the +that method handles cannot, which is listing the class members and inspecting their properties. Method handles, on the other hand, can be transformed and manipulated in a way that is not possible with Reflection API. When it comes to method invocation, there are differences related to access checking and security considerations. The @@ -612,7 +612,7 @@ Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same   ## Conclusion -In this tutorial, we have looked into the Method Handle mechanism and learned how to efficiently use it. We now know, +In this tutorial, we have looked into the method handle mechanism and learned how to efficiently use it. We now know, that method handles provide means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. From 2d37bf8b257a19c866c6c754add2ada325da7511 Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Fri, 24 May 2024 16:42:43 +0200 Subject: [PATCH 08/26] Re-word in a few places for clarity --- .../02_invoke/00_methodhandle.md | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 1d5b3d7..1e6c4ab 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -29,7 +29,7 @@ last_update: 2024-05-04   ## What are method handles Method handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, -because both the Reflection API and method handles provide means to invoke methods and constructors and access fields. +because both the Reflection API and method handles provide means to invoke methods, constructors and access fields. What exactly is a method handle? It's a direct reference to a method, constructor, or field, which can be invoked. The Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the @@ -43,7 +43,7 @@ The access checking for method handle invocations is done differently compared t each call results in access checks for the caller. For method handles, the access is only checked when the method handle is created. -It is important to keep in mind, that if the method handle is created within a context where it can access non-public +It is important to keep in mind that if the method handle is created within a context where it can access non-public members, when passed outside, it can still access those non-public members. As a result, non-public members can potentially be accessed from code where they shouldn't be accessible. It's a developer's responsibility to keep such method handles private to their context. Alternatively, the method handle can be created with access limitations right @@ -51,7 +51,7 @@ away using the appropriate lookup object.   ## Method handle lookup -To create a method handle, we first need to create a [`Lookup`](javadoc:Lookup) object, which acts as a factory for +To create a method handle we first need to create a [`Lookup`](javadoc:Lookup) object, which acts as a factory for creating method handles. Depending on how the lookup object itself or the method handles are going to be used, we can decide whether we should limit its access level. @@ -73,7 +73,7 @@ MethodHandles.Lookup lookup = MethodHandles.lookup();   ## Method type -To be able to look up a method handle, we also need to provide the type information of the method or field. The method +Tolook up a method handle we also need to provide the type information of the method or field. The method type information is represented as [`MethodType`](javadoc:MethodType) object. To instantiate a `MethodType`, we have to provide the return type as the first parameter followed by all the argument types: @@ -85,7 +85,7 @@ MethodType methodType = MethodType.methodType(int.class /* the method returns in Having the `Lookup` and the `MethodType` instances, we can look up the method handle. For instance methods, we should use [`Lookup.findVirtual`](javadoc:MethodHandles.Lookup.findVirtual(Class,String,MethodType)), and for static methods [`Lookup.findStatic`](javadoc:MethodHandles.Lookup.findStatic(Class,String,MethodType)). Both these methods accept the -following arguments: a `Class`, where the method is located, a method name represented as a `String`, and a `MethodType` +following arguments: a `Class` where the method is located, a method name represented as a `String`, and a `MethodType` instance. In the example below, we are using `Lookup.findVirtual` method to look up an instance method @@ -409,7 +409,7 @@ If we try to pass the arguments that are already prefilled, we will fail with a ### Filter arguments We can use [`MethodHandles.filterArguments`](javadoc:MethodHandles.filterArguments(MethodHandle,int,MethodHandle...)) -to apply transformations to the arguments before invocation of the target method handle. To make it work, we have to provide: +to apply transformations to arguments before invocation of the target method handle. To make it work, we have to provide: - The target method handle; - The position of the first argument to transform; @@ -418,7 +418,7 @@ to apply transformations to the arguments before invocation of the target method If certain arguments don't require transformation, we can skip them by passing `null`. It's also possible to skip the rest of the arguments entirely if we only need to transform a subset of them. -Let's take a look at the following example. +Let's reuse the method handle from the previous section and filter some its arguments before its invocation. ```java MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", @@ -433,7 +433,7 @@ private static boolean negate(boolean original) { } ``` -Let's also create a method that increments any given integer value: +and also construct a method that increments any given integer value: ```java private static int increment(int original) { @@ -441,16 +441,18 @@ private static int increment(int original) { } ``` -Method handles for these transformation methods: +We can obtain method handles for these transformation methods: ```java MethodHandle negate = lookup.findStatic(Example.class, "negate", MethodType.methodType(boolean.class, boolean.class)); MethodHandle increment = lookup.findStatic(Example.class, "increment", MethodType.methodType(int.class, int.class)); ``` -Now we can create a new method handle with transformations applied to two of the original arguments: +and use them to get a new method handle having filtered arguments: ```java +// applies filter 'increment' to argument at index 0, 'negate' to the last argument, +// and passes the result to 'targetMethodHandle' MethodHandle withFilters = MethodHandles.filterArguments(targetMethodHandle, 0, increment, null, null, negate); withFilters.invoke(3, "abc", 5L, false); // outputs "4abc5true" ``` @@ -623,4 +625,4 @@ The biggest differences are: - Method invocation is more performant with method handles due to a different access checking approach. - Method handles don't provide any tooling for listing the class members and inspecting their properties. - Method handles make it easy to wrap the direct pointers to methods and fields into more complicated logic which - includes argument and return value manipulations. \ No newline at end of file + includes argument and return value manipulations. From 5377b9d198f67eefd0c2365283ec4ed833f59cff Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Fri, 24 May 2024 17:12:57 +0200 Subject: [PATCH 09/26] Update app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md Co-authored-by: dan1st --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 1e6c4ab..4f5b75f 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -73,7 +73,7 @@ MethodHandles.Lookup lookup = MethodHandles.lookup();   ## Method type -Tolook up a method handle we also need to provide the type information of the method or field. The method +To look up a method handle we also need to provide the type information of the method or field. The method type information is represented as [`MethodType`](javadoc:MethodType) object. To instantiate a `MethodType`, we have to provide the return type as the first parameter followed by all the argument types: From 6a589d85e182c81c1879403d635398ba48e89a20 Mon Sep 17 00:00:00 2001 From: Nataliia Dziubenko Date: Fri, 24 May 2024 19:39:06 +0200 Subject: [PATCH 10/26] some rephrasing according to the review comments --- .../02_invoke/00_methodhandle.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 4f5b75f..caf6755 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -31,7 +31,7 @@ last_update: 2024-05-04 Method handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, because both the Reflection API and method handles provide means to invoke methods, constructors and access fields. -What exactly is a method handle? It's a direct reference to a method, constructor, or field, which can be invoked. +What exactly is a method handle? It's a directly invocable reference to an underlying method, constructor, or field. The Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the arguments, or transform the return values, for example. @@ -618,11 +618,9 @@ In this tutorial, we have looked into the method handle mechanism and learned ho that method handles provide means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. -The biggest differences are: +Method handles offer a performance advantage for method invocation due to a different access checking approach. However, +since access is checked only on method handle creation, method handles should be passed around with caution. -- Access checking happens only on method handle creation, which means that method handles should be passed around with - caution. -- Method invocation is more performant with method handles due to a different access checking approach. -- Method handles don't provide any tooling for listing the class members and inspecting their properties. -- Method handles make it easy to wrap the direct pointers to methods and fields into more complicated logic which - includes argument and return value manipulations. +Unlike the Reflection API, method handles don't provide any tooling for listing class members and inspecting their properties. +On the other hand, the Method Handle API allows us to wrap direct pointers to methods and fields into more complex +logic, such as argument and return value manipulations. \ No newline at end of file From d4fb492ae48db0fd7845aa738b35dfbcc576d92c Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Tue, 28 May 2024 13:50:30 +0200 Subject: [PATCH 11/26] Update app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md Co-authored-by: dan1st --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index caf6755..174faf5 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -84,7 +84,7 @@ MethodType methodType = MethodType.methodType(int.class /* the method returns in Having the `Lookup` and the `MethodType` instances, we can look up the method handle. For instance methods, we should use [`Lookup.findVirtual`](javadoc:MethodHandles.Lookup.findVirtual(Class,String,MethodType)), and for static methods -[`Lookup.findStatic`](javadoc:MethodHandles.Lookup.findStatic(Class,String,MethodType)). Both these methods accept the +[`Lookup.findStatic`](javadoc:MethodHandles.Lookup.findStatic(Class,String,MethodType)). Both of these methods accept the following arguments: a `Class` where the method is located, a method name represented as a `String`, and a `MethodType` instance. From c4ca2d6faa7934da44e8d990d558f803f1dc0b8c Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Tue, 28 May 2024 13:50:45 +0200 Subject: [PATCH 12/26] Update app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md Co-authored-by: dan1st --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 174faf5..a8b7148 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -57,7 +57,7 @@ decide whether we should limit its access level. For example, if we create a method handle pointing to a private method and that method handle is accessible from outside, so is the private method. Normally we would like to avoid that. One way is to make the lookup object and method handle -`private` too. Another option is to create the lookup object using [`MethodHandles.publicLookup`](javadoc:MethodHandles.publicLookup()) +`private` too. Another option is to create the lookup object using the [`MethodHandles.publicLookup`](javadoc:MethodHandles.publicLookup()) method, so it will only be able to search for public members in public classes within packages that are exported unconditionally: ```java From 8b14c6900fa5a260f405065a5ea07beeacf968ba Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Tue, 28 May 2024 13:50:57 +0200 Subject: [PATCH 13/26] Update app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md Co-authored-by: dan1st --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index a8b7148..c2b3911 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -258,7 +258,7 @@ The [`MethodHandles.catchException`](javadoc:MethodHandles.catchException(Method wrap a given method handle inside a provided exception handler method handle. Say, we have a method `problematicMethod` that performs some business logic, and a method `exceptionHandler` that handles -a particular exception [`IllegalArgumentException`](javadoc:IllegalArgumentException). The exception handler method must +a particular [`IllegalArgumentException`](javadoc:IllegalArgumentException). The exception handler method must return the same type as the original method. The first argument it accepts is a `Throwable` that we're interested in, after which follow the rest of the arguments that we've originally accepted: From 8cab673eb25058946c45e7aee24f72cf27da471d Mon Sep 17 00:00:00 2001 From: Ana Maria Mihalceanu Date: Tue, 28 May 2024 13:51:19 +0200 Subject: [PATCH 14/26] Update app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md Co-authored-by: dan1st --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index c2b3911..419554b 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -470,7 +470,7 @@ private static void target(int ignored, int sum, int a, int b) { } ``` -Using `foldArguments` we can pre-process subset of its arguments and insert the resulting value as another +Using `foldArguments` we can pre-process a subset of its arguments and insert the resulting value as another argument and proceed to execution of the `target` method. In our example, we have arguments `int a, int b` at the end. We can pre-process any amount of arguments, but they all From 344575ab456671cf914ac2c0cf913bbec5ff1706 Mon Sep 17 00:00:00 2001 From: Chad Arimura Date: Wed, 29 May 2024 15:41:51 -0700 Subject: [PATCH 15/26] suggestion for uppercase sentence --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 419554b..2cd771b 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -518,8 +518,7 @@ for a result placeholder in `target` method argument list. Similarly to arguments, we can use an adapter that will apply transformations to the return value. Let's imagine a situation where we have a method that returns a `String`, and we would like to channel any returned -value from this method into another method that replaces character `d` with `m` and brings the resulting value to the -uppercase. +value from this method into another method that replaces character `d` with `m` and uppercases the resulting value. Here's the method handle for the `getSomeString` method which always returns the value `"dummy"`: From 5c62b6f00aa2d3ba3dd6fa9184d5183027953af3 Mon Sep 17 00:00:00 2001 From: Chad Arimura Date: Thu, 30 May 2024 13:29:42 -0700 Subject: [PATCH 16/26] some grammar cleanup, hopefully all ok --- .../02_invoke/00_methodhandle.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 2cd771b..1b3f2ad 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -8,7 +8,7 @@ category_order: 1 layout: learn/tutorial.html subheader_select: tutorials main_css_id: learn -description: "What is method handle mechanism, how is it different from Reflection API, and what tooling does it provide." +description: "What are method handles, how are they different from the Reflection API, and what tooling do they provide?" author: ["NataliiaDziubenko"] toc: - What are method handles {intro} @@ -28,14 +28,14 @@ last_update: 2024-05-04   ## What are method handles -Method handles are a low level mechanism used for method lookup and invocation. It is often compared to reflection, -because both the Reflection API and method handles provide means to invoke methods, constructors and access fields. +Method handles are a low-level mechanism used for method lookup and invocation. They are often compared to reflection, +because both the Reflection API and method handles provide a means to invoke methods, constructors, and access fields. What exactly is a method handle? It's a directly invocable reference to an underlying method, constructor, or field. -The Method Handle API allows manipulations on top of simple pointer to the method, that allow us to insert or reorder the -arguments, or transform the return values, for example. +The Method Handle API allows manipulations on top of a simple pointer to the method that allows us to insert or reorder the +arguments, transform the return values, etc. -Let's take a closer look at what method handle mechanism can provide and how we can effectively use it. +Let's take a closer look at what method handles can provide and how we can effectively use them.   ## Access checking @@ -55,8 +55,8 @@ To create a method handle we first need to create a [`Lookup`](javadoc:Lookup) o creating method handles. Depending on how the lookup object itself or the method handles are going to be used, we can decide whether we should limit its access level. -For example, if we create a method handle pointing to a private method and that method handle is accessible from outside, -so is the private method. Normally we would like to avoid that. One way is to make the lookup object and method handle +For example, if we create a method handle pointing to a private method and that method handle is accessible from the outside, +then the private method is as well. Normally we would like to avoid that. One way is to make the lookup object and method handle `private` too. Another option is to create the lookup object using the [`MethodHandles.publicLookup`](javadoc:MethodHandles.publicLookup()) method, so it will only be able to search for public members in public classes within packages that are exported unconditionally: @@ -138,7 +138,7 @@ String result = (String) replaceMethodHandle.invoke((Object)"dummy", (Object)'d' ``` One other alternative to invoke a method handle is to use [`MethodHandle.invokeWithArguments`](javadoc:MethodHandle.invokeWithArguments(Object...)). -The result of this method invocation is equivalent to `invoke`, with the only difference that all the arguments can be +The result of this method invocation is equivalent to `invoke`, with the only difference being that all the arguments can be provided as an array or list of objects. One interesting feature of this method is that if the number of provided arguments exceeds the expected number, all the @@ -147,8 +147,8 @@ leftover arguments will be squashed into the last argument, which will be treate   ## Accessing fields It is possible to create method handles that have read or write access to fields. For instance fields, this is -facilitated by [`findGetter`](javadoc:MethodHandles.Lookup.findGetter(Class,String,Class)) and -[`findSetter`](javadoc:MethodHandles.Lookup.findSetter(Class,String,Class)) methods, and for static fields, by +facilitated by the [`findGetter`](javadoc:MethodHandles.Lookup.findGetter(Class,String,Class)) and +[`findSetter`](javadoc:MethodHandles.Lookup.findSetter(Class,String,Class)) methods, and for static fields, by the [`findStaticGetter`](javadoc:MethodHandles.Lookup.findStaticGetter(Class,String,Class)) and [`findStaticSetter`](javadoc:MethodHandles.Lookup.findStaticSetter(Class,String,Class)) methods. We don't need to provide a `MethodType` instance; instead, we should provide a single type, which is the type of the field. @@ -534,7 +534,7 @@ private static String resultTransform(String value) { } ``` -Here is the method handle for our transformator method: +Here is the method handle for our transformer method: ```java MethodHandle resultTransform = lookup.findStatic(Example.class, "resultTransform", MethodType.methodType(String.class, String.class)); @@ -614,7 +614,7 @@ Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same   ## Conclusion In this tutorial, we have looked into the method handle mechanism and learned how to efficiently use it. We now know, -that method handles provide means for efficient method invocation, but this mechanism is not meant to replace the +that method handles provide a means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. Method handles offer a performance advantage for method invocation due to a different access checking approach. However, From 89aafcd2214cff794513e1cac88ce177cf9f4058 Mon Sep 17 00:00:00 2001 From: Chad Arimura Date: Thu, 30 May 2024 14:49:46 -0700 Subject: [PATCH 17/26] some grammer, small reduction in repeated code" --- .../02_invoke/00_methodhandle.md | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 1b3f2ad..83db1f2 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -8,7 +8,7 @@ category_order: 1 layout: learn/tutorial.html subheader_select: tutorials main_css_id: learn -description: "What are method handles, how are they different from the Reflection API, and what tooling do they provide?" +description: "Method handles, how they are different from the Reflection API, and the tooling they provide." author: ["NataliiaDziubenko"] toc: - What are method handles {intro} @@ -23,7 +23,7 @@ toc: - Method Handles vs Reflection API {vsreflection} - Conversion between Reflection API and method handles {unreflect} - Conclusion {conclusion} -last_update: 2024-05-04 +last_update: 2024-05-30 ---   @@ -132,8 +132,6 @@ handle with adjusted types that would strictly match the types of provided argum invoke the adjusted method handle using `invokeExact`. ```java -MethodType replaceMethodType = MethodType.methodType(String.class, char.class, char.class); -MethodHandle replaceMethodHandle = lookup.findVirtual(String.class, "replace", replaceMethodType); String result = (String) replaceMethodHandle.invoke((Object)"dummy", (Object)'d', (Object)'m'); // would fail with `invokeExact` ``` @@ -418,14 +416,14 @@ to apply transformations to arguments before invocation of the target method han If certain arguments don't require transformation, we can skip them by passing `null`. It's also possible to skip the rest of the arguments entirely if we only need to transform a subset of them. -Let's reuse the method handle from the previous section and filter some its arguments before its invocation. +Let's reuse the method handle from the previous section and filter some of its arguments before invocation. ```java MethodHandle targetMethodHandle = lookup.findStatic(Example.class, "test", MethodType.methodType(void.class, int.class, String.class, long.class, boolean.class)); ``` -Let's create a method that transforms any `boolean` value by negating it: +Then we create a method that transforms any `boolean` value by negating it: ```java private static boolean negate(boolean original) { @@ -448,7 +446,7 @@ MethodHandle negate = lookup.findStatic(Example.class, "negate", MethodType.meth MethodHandle increment = lookup.findStatic(Example.class, "increment", MethodType.methodType(int.class, int.class)); ``` -and use them to get a new method handle having filtered arguments: +and use them to get a new method handle having filtered the arguments: ```java // applies filter 'increment' to argument at index 0, 'negate' to the last argument, @@ -471,7 +469,7 @@ private static void target(int ignored, int sum, int a, int b) { ``` Using `foldArguments` we can pre-process a subset of its arguments and insert the resulting value as another -argument and proceed to execution of the `target` method. +argument and proceed to the execution of the `target` method. In our example, we have arguments `int a, int b` at the end. We can pre-process any amount of arguments, but they all must be at the end. Let's say, we would like to calculate a sum of these two values `a` and `b`, so let's create a method @@ -499,10 +497,9 @@ MethodHandle preProcessedArguments = MethodHandles.foldArguments(targetMethodHan ``` The `foldArguments` method accepts: -- Target method handle, in our case the one pointing to `target` method; -- `int` number specifying the starting position of arguments related to folding. In our case, the `sum` argument - is located at position `1`, so we passed `1`. If we skip this argument, `pos` will default to `0`. -- Combiner method handle, in our case it's the one pointing to `sum` method. +- `MethodHandle` *target*: The target method handle, in our case the one pointing to the `target` method. +- `int` *pos*: An integer specifying the starting position of arguments related to folding. In our case, the `sum` argument is located at position `1`, so we passed `1`. If we skip this argument, `pos` will default to `0`. +- `MethodHandle` *combiner*: The combiner method handle, in our case the one pointing to the `sum` method. At the end, we can invoke the resulting method handle and pass all the arguments except `sum` which is going to be pre-calculated: @@ -512,7 +509,7 @@ preProcessedArguments.invokeExact(10000, 1, 2); // outputs: "1 + 2 equals 3 and ``` It is possible that the combiner method processes values but doesn't return anything. In this case, there is no need -for a result placeholder in `target` method argument list. +for a result placeholder in the `target` method argument list. ### Filter return value Similarly to arguments, we can use an adapter that will apply transformations to the return value. @@ -551,9 +548,11 @@ System.out.println(getSomeUppercaseString.invoke()); // outputs: "MUMMY"   ## Method Handles vs Reflection API Method handles were introduced in [JDK7](https://docs.oracle.com/javase/7/docs/index.html) as a tool to assist -compiler and language runtime developers. They were never meant to replace reflection. The Reflection API offers something +compiler and language runtime developers. They were never meant to replace reflection. + +[The Reflection API](id:api.reflection) offers something that method handles cannot, which is listing the class members and inspecting their properties. Method handles, on the -other hand, can be transformed and manipulated in a way that is not possible with Reflection API. +other hand, can be transformed and manipulated in a way that is not possible with the Reflection API. When it comes to method invocation, there are differences related to access checking and security considerations. The Reflection API performs access checking against every caller, on every call, while for method handles, access is @@ -561,6 +560,8 @@ checked only during construction. This makes invocation through method handles f However, certain precautions have to be taken so the method handle is not passed to the code where it shouldn't be accessible. +You can learn more about Reflection in [this tutorial](id:api.reflection). +   ## Conversion between Reflection API and method handles The `Lookup` object can be used to convert Reflection API objects to behaviorally equivalent method handles, which @@ -613,8 +614,7 @@ Field field = MethodHandles.reflectAs(Field.class, getterMethodHandle); // same   ## Conclusion -In this tutorial, we have looked into the method handle mechanism and learned how to efficiently use it. We now know, -that method handles provide a means for efficient method invocation, but this mechanism is not meant to replace the +In this tutorial, we have looked into the method handle mechanism and learned how to efficiently use it. We now know that method handles provide a means for efficient method invocation, but this mechanism is not meant to replace the Reflection API. Method handles offer a performance advantage for method invocation due to a different access checking approach. However, From 14f78994d0fa9bcc14398362ac4ca3b515cb2ef7 Mon Sep 17 00:00:00 2001 From: Nataliia Dziubenko Date: Sat, 1 Jun 2024 22:42:09 +0200 Subject: [PATCH 18/26] small fixes of code blocks --- .../04_mastering-the-api/02_invoke/00_methodhandle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md index 83db1f2..ba849ae 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_invoke/00_methodhandle.md @@ -282,7 +282,7 @@ other exceptions if they arise. MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle methodHandle = lookup.findStatic(Example.class, "problematicMethod", MethodType.methodType(int.class, String.class)); MethodHandle handler = lookup.findStatic(Example.class, "exceptionHandler", -MethodType.methodType(int.class, IllegalArgumentException.class, String.class)); + MethodType.methodType(int.class, IllegalArgumentException.class, String.class)); MethodHandle wrapped = MethodHandles.catchException(methodHandle, IllegalArgumentException.class, handler); System.out.println(wrapped.invoke("valid")); // outputs "1" @@ -464,7 +464,7 @@ Let's assume that we have a `target` method: ```java private static void target(int ignored, int sum, int a, int b) { - System.out.printf("%s + %s equals %s and %s is ignored%n", a, b, sum, ignored); + System.out.printf("%d + %d equals %d and %d is ignored%n", a, b, sum, ignored); } ``` From 03123b0133bf4ccaa19f8b9bf372f16122b2ea17 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 28 Jun 2024 18:43:54 +0200 Subject: [PATCH 19/26] update enums article --- .../04_classes_objects/01_enums.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md index 651a2ea..42d7591 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md @@ -17,7 +17,7 @@ toc: - Precautions {precautions} - Conclusion {conclusion} description: "Working with enums." -last_update: 2023-09-29 +last_update: 2024-06-28 author: ["DanielSchmid"] ---   @@ -32,7 +32,7 @@ No instances of the enum can be created outside of enum constants. ```java public enum DayOfWeek { - // enum constant are listed here: + // enum constants are listed here: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } ``` @@ -74,8 +74,8 @@ switch (someDay) { With [Switch Expressions](id:lang.classes-objects.switch-expression), the compiler can check whether all values of the enum are handled. If any possible value is missing in a switch expression, there will be a compiler error. -This is referred to as Exhaustiveness and can also be achieved with regular classes -through [Sealed Classes](https://openjdk.org/jeps/409). +This is referred to as exhaustiveness checking and can also be achieved with regular classes +through [Sealed Classes](https://openjdk.org/jeps/409) and [Patternmatching](/learn/pattern-matching/#switch). ```java DayOfWeek someDay = DayOfWeek.FRIDAY; @@ -99,7 +99,8 @@ Arguments to the constructor are passed in parenthesis after the declaration of ```java public enum DayOfWeek { - MONDAY("MON"), TUESDAY("TUE"), WEDNESDAY("WED"), THURSDAY("THU"), FRIDAY("FRI"), SATURDAY("SAT"), SUNDAY("SUN"); + MONDAY("MON"), TUESDAY("TUE"), WEDNESDAY("WED"), THURSDAY("THU"), FRIDAY("FRI"), + SATURDAY("SAT"), SUNDAY("SUN"); private final String abbreviation; @@ -140,9 +141,9 @@ This allows for comparing instances of enums as well as sorting or searching. ```java public void compareDayOfWeek(DayOfWeek dayOfWeek){ int comparison = dayOfWeek.compareTo(DayOfWeek.WEDNESDAY); - if ( comparison < 0) { + if (comparison < 0) { System.out.println("It's before the middle of the work week."); - } else if(comparison > 0){ + } else if (comparison > 0) { System.out.println("It's after the middle of the work week."); } else { System.out.println("It's the middle of the work week."); @@ -210,6 +211,6 @@ and reading these configuration files in the program in cases like this.   ## Conclusion -Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, and maintainable, and work well with other newer modern features like [Switch Expressions](id:lang.classes-objects.switch-expression). Another special class is the Record class introduced in Java 19. Visit our [Records tutorial](id:lang.records) to learn more. +Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, maintainable and works well with other modern Java features like [Switch Expressions](id:lang.classes-objects.switch-expression). Another special class is the Record class introduced in Java 19. Visit our [Records tutorial](id:lang.records) to learn more. To learn more about enums, visit the [`java.lang.Enum`](javadoc:Enum) javadoc. \ No newline at end of file From ac01cc32ae484053e11314de2b79103595e3b591 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Mon, 8 Jul 2024 20:28:19 +0200 Subject: [PATCH 20/26] address review comments --- .../04_classes_objects/01_enums.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md index 42d7591..3704c74 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md @@ -17,7 +17,7 @@ toc: - Precautions {precautions} - Conclusion {conclusion} description: "Working with enums." -last_update: 2024-06-28 +last_update: 2024-07-08 author: ["DanielSchmid"] ---   @@ -71,11 +71,11 @@ switch (someDay) { } ``` -With [Switch Expressions](id:lang.classes-objects.switch-expression), +With [switch expressions](id:lang.classes-objects.switch-expression), the compiler can check whether all values of the enum are handled. If any possible value is missing in a switch expression, there will be a compiler error. This is referred to as exhaustiveness checking and can also be achieved with regular classes -through [Sealed Classes](https://openjdk.org/jeps/409) and [Patternmatching](/learn/pattern-matching/#switch). +through [sealed classes](https://openjdk.org/jeps/409) and [patternmatching](/learn/pattern-matching/#switch). ```java DayOfWeek someDay = DayOfWeek.FRIDAY; From 0557e2f00c55306d8ad13f2494d4353db0787c46 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Mon, 8 Jul 2024 21:21:11 +0200 Subject: [PATCH 21/26] add missing space --- .../04_classes_objects/01_enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md index 3704c74..88a6381 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md @@ -75,7 +75,7 @@ With [switch expressions](id:lang.classes-objects.switch-expression), the compiler can check whether all values of the enum are handled. If any possible value is missing in a switch expression, there will be a compiler error. This is referred to as exhaustiveness checking and can also be achieved with regular classes -through [sealed classes](https://openjdk.org/jeps/409) and [patternmatching](/learn/pattern-matching/#switch). +through [sealed classes](https://openjdk.org/jeps/409) and [pattern matching](/learn/pattern-matching/#switch). ```java DayOfWeek someDay = DayOfWeek.FRIDAY; From 8f7385046c67b611b30da62b9e8022e91acb7a54 Mon Sep 17 00:00:00 2001 From: ammbra Date: Wed, 25 Sep 2024 09:47:29 +0200 Subject: [PATCH 22/26] Update documentation to JDK 23. --- app/data/javadoc.json | 208 +++++++++++++++--- app/data/javafxdoc.json | 4 +- app/data/jep.json | 73 ++++++ app/pages/future/innovation/index.md | 14 +- .../04_classes_objects/01_enums.md | 8 +- .../02_modern_io/01_modern_io.md | 93 ++++---- gulpfile.js | 31 +++ 7 files changed, 348 insertions(+), 83 deletions(-) create mode 100644 app/data/jep.json diff --git a/app/data/javadoc.json b/app/data/javadoc.json index a4e40f1..62150f6 100644 --- a/app/data/javadoc.json +++ b/app/data/javadoc.json @@ -1,8 +1,10 @@ { - "current_release": "22", + "current_release": "23", "java-documentation": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/", + "java-documentation-summary": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/api/java.naming/module-summary.html", "javadoc_root": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/api/", + "jdk-release-note": "https://www.oracle.com/java/technologies/javase/jdk-relnotes-index.html", "security-standard-names": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/security/standard-names.html", "jre-jdk-cryptoroadmap": "https://www.java.com/en/jre-jdk-cryptoroadmap.html", @@ -145,21 +147,12 @@ "Cloneable": "java.base/java/lang/Cloneable.html", "CloneNotSupportedException": "java.base/java/lang/CloneNotSupportedException.html", - "Class": "java.base/java/lang/Class.html", - "Class.getFields()": "java.base/java/lang/Class.html#getFields()", - "Class.getInterfaces()": "java.base/java/lang/Class.html#getInterfaces()", - "Class.getMethods()": "java.base/java/lang/Class.html#getMethods()", - "Class.getSimpleName()": "java.base/java/lang/Class.html#getSimpleName()", - "Class.getSuperclass()": "java.base/java/lang/Class.html#getSuperclass()", - "Class.isAnnotation()": "java.base/java/lang/Class.html#isAnnotation()", - "Class.isEnum()": "java.base/java/lang/Class.html#isEnum()", - "Class.isInterface()": "java.base/java/lang/Class.html#isInterface()", - "Record": "java.base/java/lang/Record.html", "System": "java.base/java/lang/System.html", "System.out": "java.base/java/lang/System.html#out", "System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)": "java.base/java/lang/System.html#arraycopy(java.lang.Object,int,java.lang.Object,int,int)", + "System.console()": "java.base/java/lang/System.html#console()", "Long": "java.base/java/lang/Long.html", "Long.compareTo(Long)": "java.base/java/lang/Long.html#compareTo(java.lang.Long)", @@ -420,9 +413,7 @@ "FileTime": "java.base/java/nio/file/attribute/FileTime.html", - "Iterable": "java.base/java/lang/Iterable.html", - "Iterable.forEach()": "java.base/java/lang/Iterable.html#forEach(java.util.function.Consumer)", - "Iterable.iterator()": "java.base/java/lang/Iterable.html#iterator()", + "Console": "java.base/java/io/Console.html", "Path": "java.base/java/nio/file/Path.html", "Path.toFile()": "java.base/java/nio/file/Path.html#toFile()", @@ -519,7 +510,9 @@ "String.regionMatches(int,String,int,int)": "java.base/java/lang/String.html#regionMatches(int,java.lang.String,int,int)", "String.equals(Object)": "java.base/java/lang/String.html#equals(java.lang.Object)", "String.regionMatches(boolean,int,String,int,int)": "java.base/java/lang/String.html#regionMatches(boolean,int,java.lang.String,int,int)", - "String.join(CharSequence,CharSequence)": "java.base/java/lang/String.html#join(java.lang.CharSequence,java.lang.CharSequence)", + "String.join(CharSequence,CharSequence)": "java.base/java/lang/String.html#join(java.lang.CharSequence,java.lang.CharSequence...)", + "String.join(CharSequence,Iterable)": "java.base/java/lang/String.html#join(java.lang.CharSequence,java.lang.Iterable)", + "String.valueOf(Object)": "java.base/java/lang/String.html#valueOf(java.lang.Object)", "StringIndexOutOfBoundsException": "java.base/java/lang/StringIndexOutOfBoundsException.html", @@ -528,8 +521,8 @@ "StringBuilder": "java.base/java/lang/StringBuilder.html", "StringBuilder()": "java.base/java/lang/StringBuilder.html#%3Cinit%3E()", - "StringBuilder(CharSequence)": "java/lang/StringBuilder.html#%3Cinit%3E(java.lang.CharSequence)", - "StringBuilder(String)": "java/lang/StringBuilder.html#%3Cinit%3E(java.lang.String)", + "StringBuilder(CharSequence)": "java.base/java/lang/StringBuilder.html#%3Cinit%3E(java.lang.CharSequence)", + "StringBuilder(String)": "java.base/java/lang/StringBuilder.html#%3Cinit%3E(java.lang.String)", "StringBuilder(int)": "java.base/java/lang/StringBuilder.html#%3Cinit%3E(int)", "StringBuilder.length()": "java.base/java/lang/StringBuilder.html#length()", "StringBuilder.capacity()": "java.base/java/lang/StringBuilder.html#capacity()", @@ -570,6 +563,7 @@ "Externalizable": "java.base/java/io/Externalizable.html", "Writer": "java.base/java/io/Writer.html", + "FileWriter": "java.base/java/io/FileWriter.html", "FileReader": "java.base/java/io/FileReader.html", "FilterWriter": "java.base/java/io/FilterWriter.html", "CharArrayReader": "java.base/java/io/CharArrayReader.html", @@ -941,7 +935,8 @@ "TimeZone.toZoneId()": "java.base/java/util/TimeZone.html#toZoneId()", "DateTimeParseException": "java.base/java/time/format/DateTimeParseException.html", - "DateTimeException": "java.base/java/time/format/DateTimeException.html", + + "DateTimeException": "java.base/java/time/DateTimeException.html", "DecimalFormat": "java.base/java/text/DecimalFormat.html", @@ -1067,9 +1062,6 @@ "RetentionPolicy.RUNTIME": "java.base/java/lang/annotation/RetentionPolicy.html#RUNTIME", "RetentionPolicy.SOURCE": "java.base/java/lang/annotation/RetentionPolicy.html#SOURCE", "Target": "java.base/java/lang/annotation/Target.html", - "AnnotatedElement": "java.base/java/lang/reflect/AnnotatedElement.html", - "AnnotatedElement.getAnnotation(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#getAnnotation(java.lang.Class)", - "AnnotatedElement.getAnnotationsByType(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#getAnnotationsByType(java.lang.Class)", "IllegalArgumentException": "java.base/java/lang/IllegalArgumentException.html", "IllegalStateException": "java.base/java/lang/IllegalStateException.html", @@ -1080,11 +1072,19 @@ "ArrayDeque": "java.base/java/util/ArrayDeque.html", "Iterator": "java.base/java/util/Iterator.html", + "Iterator.hasNext()": "java.base/java/util/Iterator.html#hasNext()", + "Iterator.next()": "java.base/java/util/Iterator.html#next()", + "Iterator.remove()": "java.base/java/util/Iterator.html#remove()", "Iterator.forEachRemaining(Consumer)": "java.base/java/util/Iterator.html#forEachRemaining(java.util.function.Consumer)", + "Iterable": "java.base/java/lang/Iterable.html", + "Iterable.forEach()": "java.base/java/lang/Iterable.html#forEach(java.util.function.Consumer)", + "Iterable.iterator()": "java.base/java/lang/Iterable.html#iterator()", + "Collection": "java.base/java/util/Collection.html", "Collection.removeIf(Predicate)": "java.base/java/util/Collection.html#removeIf(java.util.function.Predicate)", "Collection.add(E)": "java.base/java/util/Collection.html#add(E)", + "Collection.remove(Object)": "java.base/java/util/Collection.html#remove(java.lang.Object)", "Collection.addAll(Collection)": "java.base/java/util/Collection.html#addAll(java.util.Collection)", "Collection.clear()": "java.base/java/util/Collection.html#clear()", "Collection.contains(Object)": "java.base/java/util/Collection.html#contains(java.lang.Object)", @@ -1138,11 +1138,6 @@ "IdentityHashMap": "java.base/java/util/IdentityHashMap.html", - "Iterator": "java.base/java/util/Iterator.html", - "Iterator.hasNext()": "java.base/java/util/Iterator.html#hasNext()", - "Iterator.next()": "java.base/java/util/Iterator.html#next()", - "Iterator.remove()": "java.base/java/util/Iterator.html#remove()", - "LinkedHashMap": "java.base/java/util/LinkedHashMap.html", "List": "java.base/java/util/List.html", @@ -1223,6 +1218,9 @@ "NavigableMap.subMap(K,boolean,K,boolean)": "java.base/java/util/NavigableMap.html#subMap(K,boolean,K,boolean)", "NavigableMap.tailMap(K)": "java.base/java/util/NavigableMap.html#tailMap(K)", + "ConcurrentMap": "java.base/java/util/concurrent/ConcurrentMap.html", + "ConcurrentMap.putIfAbsent()": "java.base/java/util/concurrent/ConcurrentMap.html#putIfAbsent()", + "NavigableSet": "java.base/java/util/NavigableSet.html", "NavigableSet.ceiling(E)": "java.base/java/util/NavigableSet.html#ceiling(E)", "NavigableSet.descendingIterator()": "java.base/java/util/NavigableSet.html#descendingIterator()", @@ -1286,6 +1284,7 @@ "NumberFormatException": "java.base/java/lang/NumberFormatException.html", "OutOfMemoryError": "java.base/java/lang/OutOfMemoryError.html", + "Character.UnicodeBlock": "java.base/java/lang/Character.UnicodeBlock.html", "Character.toString(int)": "java.base/java/lang/Character.html#toString(int)", "Charset": "java.base/java/nio/charset/Charset.html", @@ -1448,6 +1447,157 @@ "MBeanServerConnection": "java.management/javax/management/MBeanServerConnection.html", "SimpleFileServer": "jdk.httpserver/com/sun/net/httpserver/SimpleFileServer", - "Method": "java.base/java/lang/reflect/Method.html", - "Method.invoke()": "java.base/java/lang/reflect/Method.html#invoke(java.lang.Object,java.lang.Object...)" -} \ No newline at end of file + "Lookup": "java.base/java/lang/invoke/MethodHandles.Lookup.html", + "MethodType": "java.base/java/lang/invoke/MethodType.html", + "MethodHandles": "java.base/java/lang/invoke/MethodHandles.html", + "MethodHandles.Lookup.findVirtual(Class,String,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findVirtual(java.lang.Class,java.lang.String,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findStatic(Class,String,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStatic(java.lang.Class,java.lang.String,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findConstructor(Class,MethodType)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findConstructor(java.lang.Class,java.lang.invoke.MethodType)", + "MethodHandles.Lookup.findGetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findGetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findSetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findSetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticGetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticGetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticSetter(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticSetter(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findVarHandle(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findVarHandle(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.findStaticVarHandle(Class,String,Class)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#findStaticVarHandle(java.lang.Class,java.lang.String,java.lang.Class)", + "MethodHandles.Lookup.unreflect(Method)": "java.base/java/lang/invoke/MethodHandles.Lookup.html#unreflect(java.lang.reflect.Method)", + "MethodHandles.publicLookup()": "java.base/java/lang/invoke/MethodHandles.html#publicLookup()", + "MethodHandles.tryFinally(MethodHandle,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#tryFinally(java.lang.invoke.MethodHandle,java.lang.invoke.MethodHandle)", + "MethodHandles.permuteArguments(MethodHandle,MethodType,int...)": "java.base/java/lang/invoke/MethodHandles.html#permuteArguments(java.lang.invoke.MethodHandle,java.lang.invoke.MethodType,int...)", + "MethodHandles.insertArguments(MethodHandle,int,Object...)": "java.base/java/lang/invoke/MethodHandles.html#insertArguments(java.lang.invoke.MethodHandle,int,java.lang.Object...)", + "MethodHandles.filterArguments(MethodHandle,int,MethodHandle...)": "java.base/java/lang/invoke/MethodHandles.html#filterArguments(java.lang.invoke.MethodHandle,int,java.lang.invoke.MethodHandle...)", + "MethodHandles.foldArguments(MethodHandle,int,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#foldArguments(java.lang.invoke.MethodHandle,int,java.lang.invoke.MethodHandle)", + "MethodHandles.reflectAs(Class,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#reflectAs(java.lang.Class,java.lang.invoke.MethodHandle)", + "MethodHandles.arrayConstructor(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayConstructor(java.lang.Class)", + "MethodHandles.arrayElementSetter(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayElementSetter(java.lang.Class)", + "MethodHandles.arrayElementGetter(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayElementGetter(java.lang.Class)", + "MethodHandles.catchException(MethodHandle,Class,MethodHandle)": "java.base/java/lang/invoke/MethodHandles.html#catchException(java.lang.invoke.MethodHandle,java.lang.Class,java.lang.invoke.MethodHandle)", + "MethodHandles.arrayLength(Class)": "java.base/java/lang/invoke/MethodHandles.html#arrayLength(java.lang.Class)", + "MethodHandle.invoke(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invoke(java.lang.Object...)", + "MethodHandle.invokeExact(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invokeExact(java.lang.Object...)", + "MethodHandle.invokeWithArguments(Object...)": "java.base/java/lang/invoke/MethodHandle.html#invokeWithArguments(java.lang.Object...)", + "MethodHandle.asType(MethodType)": "java.base/java/lang/invoke/MethodHandle.html#asType(java.lang.invoke.MethodType)", + "VarHandle": "java.base/java/lang/invoke/VarHandle.html", + "WrongMethodTypeException": "java.base/java/lang/invoke/WrongMethodTypeException.html", + + "ReflectPermission": "java.base/java/lang/reflect/ReflectPermission.html", + + "java.lang.reflect": "java.base/java/lang/reflect/package-summary.html", + + "Class": "java.base/java/lang/Class.html", + "Class.getFields()": "java.base/java/lang/Class.html#getFields()", + "Class.getInterfaces()": "java.base/java/lang/Class.html#getInterfaces()", + "Class.getMethods()": "java.base/java/lang/Class.html#getMethods()", + "Class.getSimpleName()": "java.base/java/lang/Class.html#getSimpleName()", + "Class.getSuperclass()": "java.base/java/lang/Class.html#getSuperclass()", + "Class.isAnnotation()": "java.base/java/lang/Class.html#isAnnotation()", + "Class.isEnum()": "java.base/java/lang/Class.html#isEnum()", + "Class.isInterface()": "java.base/java/lang/Class.html#isInterface()", + "Class.toString()": "java.base/java/lang/Class.html#toString()", + "Class.forName(String)": "java.base/java/lang/Class.html#forName(java.lang.String)", + "Class.getName()": "java.base/java/lang/Class.html#getName()", + "Class.getCanonicalName()": "java.base/java/lang/Class.html#getCanonicalName()", + "Class.getTypeName()": "java.base/java/lang/Class.html#getTypeName()", + "Class.forPrimitiveName()": "java.base/java/lang/Class.html#forPrimitiveName()", + "Class.getClasses()": "java.base/java/lang/Class.html#getClasses()", + "Class.getEnclosingClass()": "java.base/java/lang/Class.html#getEnclosingClass()", + "Class.getDeclaredClasses()": "java.base/java/lang/Class.html#getDeclaredClasses()", + "Class.getDeclaringClass()": "java.base/java/lang/Class.html#getDeclaringClass()", + "Class.getModifiers()": "java.base/java/lang/Class.html#getModifiers()", + "Class.getDeclaredField(String)": "java.base/java/lang/Class.html#getDeclaredField(java.lang.String)", + "Class.getField(String)": "java.base/java/lang/Class.html#getField(java.lang.String)", + "Class.getDeclaredFields()": "java.base/java/lang/Class.html#getDeclaredFields()", + "Class.getDeclaredMethod(String,Class...)": "java.base/java/lang/Class.html#getDeclaredMethod(java.lang.String,java.lang.Class...)", + "Class.getMethod(String,Class...)": "java.base/java/lang/Class.html#getMethod(java.lang.String,java.lang.Class...)", + "Class.getDeclaredMethods()": "java.base/java/lang/Class.html#getDeclaredMethods()", + "Class.getDeclaredConstructor(Class...)": "java.base/java/lang/Class.html#getDeclaredConstructor(java.lang.Class...)", + "Class.getConstructor(Class...)": "java.base/java/lang/Class.html#getConstructor(java.lang.Class...)", + "Class.getDeclaredConstructors()": "java.base/java/lang/Class.html#getDeclaredConstructors()", + "Class.getConstructors()": "java.base/java/lang/Class.html#getConstructors()", + "Class.newInstance()": "java.base/java/lang/Class.html#newInstance()", + "Class.isRecord()": "java.base/java/lang/Class.html#isRecord()", + "Class.getEnumConstants()": "java.base/java/lang/Class.html#getEnumConstants()", + "Class.isArray()": "java.base/java/lang/Class.html#isArray()", + "Class.getComponentType()": "java.base/java/lang/Class.html#getComponentType()", + + "Void": "java.base/java/lang/Void.html", + "Void.TYPE": "java.base/java/lang/Void.html#TYPE", + + "Type": "java.base/java/lang/reflect/Type.html", + "Type.getTypeName()": "java.base/java/lang/reflect/Type.html#getTypeName()", + + "Member": "java.base/java/lang/reflect/Member.html", + "Member.accessFlags()": "java.base/java/lang/reflect/Member.html#accessFlags()", + "Member.getDeclaringClass()": "java.base/java/lang/reflect/Member.html#getDeclaringClass()", + + "Field": "java.base/java/lang/reflect/Field.html", + "Field.getFields()": "java.base/java/lang/reflect/Field.html#getFields()", + "Field.getType()": "java.base/java/lang/reflect/Field.html#getType()", + "Field.getGenericType()": "java.base/java/lang/reflect/Field.html#getGenericType()", + "Field.getModifiers()": "java.base/java/lang/reflect/Field.html#getModifiers()", + "Field.accessFlags()": "java.base/java/lang/reflect/Field.html#accessFlags()", + "Field.get(Object)": "java.base/java/lang/reflect/Field.html#get(java.lang.Object)", + "Field.set(Object,Object)": "java.base/java/lang/reflect/Field.html#set(java.lang.Object,java.lang.Object)", + "Field.setInt(Object,int)": "java.base/java/lang/reflect/Field.html#setInt(java.lang.Object,int)", + "Field.getInt(Object)": "java.base/java/lang/reflect/Field.html#getInt(java.lang.Object)", + "Field.setAccessible()": "java.base/java/lang/reflect/Field.html#setAccessible()", + "Field.isEnumConstant()": "java.base/java/lang/reflect/Field.html#isEnumConstant()", + + "Method": "java.base/java/lang/reflect/Method.html", + "Method.invoke(Object,Object...)": "java.base/java/lang/reflect/Method.html#invoke(java.lang.Object,java.lang.Object...)", + "Method.invoke()": "java.base/java/lang/reflect/Method.html#invoke()", + "Method.getReturnType()": "java.base/java/lang/reflect/Method.html#getReturnType()", + "Method.getGenericReturnType()": "java.base/java/lang/reflect/Method.html#getGenericReturnType()", + "Method.getGenericExceptionTypes()": "java.base/java/lang/reflect/Method.html#getGenericExceptionTypes()", + "Method.getModifiers()": "java.base/java/lang/reflect/Method.html#getModifiers()", + "Method.setAccessible(boolean)": "java.base/java/lang/reflect/Method.html#setAccessible(boolean)", + + "Constructor": "java.base/java/lang/reflect/Constructor.html", + "Constructor.newInstance()": "java.base/java/lang/reflect/Constructor.html#newInstance()", + "Constructor.getParameterTypes()": "java.base/java/lang/reflect/Constructor.html#getParameterTypes()", + "Constructor.getGenericParameterTypes()": "java.base/java/lang/reflect/Constructor.html#getGenericParameterTypes()", + + "RecordComponent": "java.base/java/lang/reflect/RecordComponent.html", + "RecordComponent.getDeclaringRecord()": "java.base/java/lang/reflect/RecordComponent.html#getDeclaringRecord()", + "RecordComponent.getAccessor()": "java.base/java/lang/reflect/RecordComponent.html#getAccessor()", + "RecordComponent.getName()": "java.base/java/lang/reflect/RecordComponent.html#getName()", + "RecordComponent.getType()": "java.base/java/lang/reflect/RecordComponent.html#getType()", + "RecordComponent.getGenericType()": "java.base/java/lang/reflect/RecordComponent.html#getGenericType()", + + "Array": "java.base/java/lang/reflect/Array.html", + "Array.newInstance()": "java.base/java/lang/reflect/Array.html#newInstance()", + "Array.newInstance(Class,int)": "java.base/java/lang/reflect/Array.html#newInstance(java.lang.Class,int)", + "Array.getLength(Object)": "java.base/java/lang/reflect/Array.html#getLength(java.lang.Object)", + "Array.set(Object,int,Object)": "java.base/java/lang/reflect/Array.html#set(java.lang.Object,int,java.lang.Object)", + "Array.get(Object,int)": "java.base/java/lang/reflect/Array.html#set(java.lang.Object,int)", + "Array.setDouble(Object,int,double)": "java.base/java/lang/reflect/Array.html#setDouble(java.lang.Object,int,double)", + "Array.getDouble(Object,int)": "java.base/java/lang/reflect/Array.html#getDouble(java.lang.Object,int)", + + "AnnotatedElement": "java.base/java/lang/reflect/AnnotatedElement.html", + "AnnotatedElement.getDeclaredAnnotations()": "java.base/java/lang/reflect/AnnotatedElement.html#getDeclaredAnnotations()", + "AnnotatedElement.getAnnotations()": "java.base/java/lang/reflect/AnnotatedElement.html#getAnnotations()", + "AnnotatedElement.isAnnotationPresent(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#isAnnotationPresent(java.lang.Class)", + "AnnotatedElement.getAnnotation(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#getAnnotation(java.lang.Class)", + "AnnotatedElement.getDeclaredAnnotation(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#getDeclaredAnnotation(java.lang.Class)", + "AnnotatedElement.getAnnotationsByType(Class)": "java.base/java/lang/reflect/AnnotatedElement.html#getAnnotationsByType(java.lang.Class)", + + "AnnotatedType": "java.base/java/lang/reflect/AnnotatedType.html", + + "Proxy": "java.base/java/lang/reflect/Proxy.html", + + "Executable": "java.base/java/lang/reflect/Executable.html", + "Executable.getParameters()": "java.base/java/lang/reflect/Executable.html#getParameters()", + + "AccessFlag": "java.base/java/lang/reflect/AccessFlag.html", + + "Modifier": "java.base/java/lang/reflect/Modifier.html", + + "ClassNotFoundException": "java.base/java/lang/ClassNotFoundException.html", + "InstantiationException": "java.base/java/lang/InstantiationException.html", + "InvocationTargetException": "java.base/java/lang/reflect/InvocationTargetException.html", + "InvocationTargetException.getTargetException()": "java.base/java/lang/reflect/InvocationTargetException.html#getTargetException()", + + "jdk.jshell": "jdk.jshell/module-summary.html", + + "jdeps-man": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jdeps.html#options-to-filter-classes-to-be-analyzed", + "jlink-man": "https://docs.oracle.com/en/java/javase/@@CURRENT_RELEASE@@/docs/specs/man/jlink.html" +} diff --git a/app/data/javafxdoc.json b/app/data/javafxdoc.json index 557e45a..dfc5f19 100644 --- a/app/data/javafxdoc.json +++ b/app/data/javafxdoc.json @@ -1,6 +1,6 @@ { - "current_release": "21.0.1", - "release_uuid": "e5ab43c6aed54893b0840c1f2dcfca4d", + "current_release": "23", + "release_uuid": "343fae14109c42b09c0437fc90a10d4b", "javafxdoc_root": "https://download.java.net/java/GA/javafx@@CURRENT_RELEASE@@/@@RELEASE_UUID@@/docs/api/", diff --git a/app/data/jep.json b/app/data/jep.json new file mode 100644 index 0000000..80bcff7 --- /dev/null +++ b/app/data/jep.json @@ -0,0 +1,73 @@ +{ + "root": "https://openjdk.org/jeps/", + + "1": "JDK Enhancement-Proposal & Roadmap Process", + "11": "Incubator Modules", + "12": "Preview Features", + + "110": "HTTP/2 Client (Incubator)", + + "220": "Modular Run-Time Images", + "225": "Javadoc Search", + "286": "Local-Variable Type Inference", + + "310": "Application Class-Data Sharing", + "321": "HTTP Client", + "325": "Switch Expressions (Preview)", + "326": "Raw String Literals (Preview)", + "328": "Flight Recorder", + "333": "ZGC: A Scalable Low-Latency Garbage Collector (Experimental)", + "345": "NUMA-Aware Memory Allocation for G1", + "346": "Promptly Return Unused Committed Memory from G1", + "349": "JFR Event Streaming", + "354": "Switch Expressions (Second Preview)", + "357": "Migrate from Mercurial to Git", + "358": "Helpful NullPointerExceptions", + "361": "Switch Expressions", + "363": "Remove the Concurrent Mark Sweep (CMS) Garbage Collector", + "369": "Migrate to GitHub", + "372": "Remove the Nashorn JavaScript Engine", + "374": "Disable and Deprecate Biased Locking", + "376": "ZGC: Concurrent Thread-Stack Processing", + "377": "ZGC: A Scalable Low-Latency Garbage Collector (Production)", + "384": "Records (Second Preview)", + "387": "Elastic Metaspace", + "391": "macOS/AArch64 Port", + "395": "Records", + "397": "Sealed Classes (Second Preview)", + "398": "Deprecate the Applet for Removal", + + "400": "UTF-8 by Default", + "407": "Remove RMI Activation", + "408": "Simple Web Server", + "409": "Sealed Classes", + "411": "Deprecate the Security Manager for Removal", + "413": "Code Snippets in Java API Documentation", + "415": "Context-Specific Deserialization Filters", + "416": "Reimplement Core Reflection with Method Handle", + "418": "Internet-Address Resolution API", + "421": "Deprecate Finalization for Removal", + "445": { + "title": "Implicitly Declared Classes and Instance Main Methods (Second Preview)", + "status": "preview", + "version": "22" + }, + "460": { + "title": "Vector API (Seventh Incubator)", + "status": "incubator", + "version": "22" + }, + "463": { + "title": "Implicitly Declared Classes and Instance Main Methods (Second Preview)", + "status": "preview", + "version": "22" + }, + + "481": { + "title": "Scoped Values (Third Preview)", + "status": "preview", + "version": "22" + }, + + "8300604": "JEP draft: Preview Features: A Look Back, and A Look Ahead" +} \ No newline at end of file diff --git a/app/pages/future/innovation/index.md b/app/pages/future/innovation/index.md index e86051e..8825b54 100644 --- a/app/pages/future/innovation/index.md +++ b/app/pages/future/innovation/index.md @@ -7,11 +7,11 @@ subheader_select: innovation ## Amber -The goal of Project Amber is to explore and incubate smaller, productivity-oriented Java language features that have been accepted as candidate JEPs under the [OpenJDK JEP process](https://openjdk.java.net/jeps/1). This Project is sponsored by the [Compiler Group](https://openjdk.java.net/groups/compiler/). +The goal of Project Amber is to explore and incubate smaller, productivity-oriented Java language features that have been accepted as candidate JEPs under [JDK Enhancement-Proposal & Roadmap Process](jep:1). This Project is sponsored by the [Compiler Group](https://openjdk.org/groups/compiler/). -Most Project Amber features go through at least one round of Preview before becoming an official part of Java SE. See [JEP 12](https://openjdk.java.net/jeps/12) for an explanation of the Preview process, and [our tutorial](id:new_features.using_preview) on how to use preview features. For a given feature, there are separate JEPs for each round of preview and for final standardization. +Most Project Amber features go through at least one round of Preview before becoming an official part of Java SE. See [Preview Features](jep:12) for an explanation of the Preview process, and [our tutorial](id:new_features.using_preview) on how to use preview features. For a given feature, there are separate JEPs for each round of preview and for final standardization. -Learn more at Project Amber's [Wiki](https://openjdk.java.net/projects/amber/), as well as Inside.java's [Amber page](https://inside.java/tag/amber). +Learn more at Project Amber's [Wiki](https://openjdk.org/projects/amber/), as well as Inside.java's [Amber page](https://inside.java/tag/amber). ## Loom @@ -23,7 +23,7 @@ Project Loom is to intended to explore, incubate and deliver Java VM features an This OpenJDK project is sponsored by the HotSpot Group. -Learn more at Project Loom's [Wiki](https://wiki.openjdk.java.net/display/loom/Main), as well as Inside.java's [Loom page](https://inside.java/tag/loom). +Learn more at Project Loom's [Wiki](https://wiki.openjdk.org/display/loom/Main), as well as Inside.java's [Loom page](https://inside.java/tag/loom). ## Panama @@ -43,7 +43,7 @@ To this end, Project Panama will include most or all of these components: * tooling or wrapper interposition for safety * exploratory work with difficult-to-integrate native libraries -Learn more at Project Panama's [Wiki](https://openjdk.java.net/projects/panama/), as well as Inside.java's [Panama page](https://inside.java/tag/panama). +Learn more at Project Panama's [Wiki](https://openjdk.org/projects/panama/), as well as Inside.java's [Panama page](https://inside.java/tag/panama). ## Valhalla @@ -58,7 +58,7 @@ The three main goals are: A number of people describe Valhalla recently as being "primarily about performance". While it is understandable why people might come to that conclusion -- many of the motivations for Valhalla are, in fact, rooted in performance considerations -- this characterization misses something very important. Yes, performance is an important part of the story -- but so are safety, abstraction, encapsulation, expressiveness, maintainability, and compatible library evolution. -Learn more at the Valhalla Project [Wiki](https://wiki.openjdk.java.net/display/valhalla/Main), as well as Inside.java's [Valhalla page](https://inside.java/tag/valhalla). +Learn more at the Valhalla Project [Wiki](https://wiki.openjdk.org/display/valhalla/Main), as well as Inside.java's [Valhalla page](https://inside.java/tag/valhalla). ## ZGC @@ -81,5 +81,5 @@ At a glance, ZGC is: At its core, ZGC is a concurrent garbage collector, meaning all heavy lifting work is done while Java threads continue to execute. This greatly limits the impact garbage collection will have on your application's response time. -Learn more at the ZGC [Wiki](https://wiki.openjdk.java.net/display/zgc/Main), as well as Inside.java's [GC page](https://inside.java/tag/gc). +Learn more at the ZGC [Wiki](https://wiki.openjdk.org/display/zgc/Main), as well as Inside.java's [GC page](https://inside.java/tag/gc). diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md index 651a2ea..ddfac6e 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md @@ -17,7 +17,7 @@ toc: - Precautions {precautions} - Conclusion {conclusion} description: "Working with enums." -last_update: 2023-09-29 +last_update: 2023-10-02 author: ["DanielSchmid"] ---   @@ -71,11 +71,11 @@ switch (someDay) { } ``` -With [Switch Expressions](id:lang.classes-objects.switch-expression), +With [Switch Expressions](id:lang.basics.switch_expressions), the compiler can check whether all values of the enum are handled. If any possible value is missing in a switch expression, there will be a compiler error. This is referred to as Exhaustiveness and can also be achieved with regular classes -through [Sealed Classes](https://openjdk.org/jeps/409). +through [Sealed Classes](jep:409). ```java DayOfWeek someDay = DayOfWeek.FRIDAY; @@ -210,6 +210,6 @@ and reading these configuration files in the program in cases like this.   ## Conclusion -Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, and maintainable, and work well with other newer modern features like [Switch Expressions](id:lang.classes-objects.switch-expression). Another special class is the Record class introduced in Java 19. Visit our [Records tutorial](id:lang.records) to learn more. +Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, and maintainable, and work well with other newer modern features like [Switch Expressions](id:lang.basics.switch_expressions). Another special class is the Record class introduced in Java 19. Visit our [Records tutorial](id:lang.records) to learn more. To learn more about enums, visit the [`java.lang.Enum`](javadoc:Enum) javadoc. \ No newline at end of file diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md index 54e86ee..5bd0496 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md @@ -7,13 +7,19 @@ category_order: 2 layout: learn/tutorial.html subheader_select: tutorials main_css_id: learn +toc: + - Introduction {introduction} + - Reading Text Files {reading-text-files} + - Writing Text Files {writing-text-files} + - The Files API {the-files-api} + - Conclusion {conclusion} last_update: 2024-04-24 description: "This article focuses on tasks that application programmers are likely to encounter, particularly in web applications, such as reading and writing text files, reading text, images, JSON from the web, and more." author: ["CayHorstmann"] --- -  +  ## Introduction This article focuses on tasks that application programmers are likely to encounter, particularly in web applications, such as: @@ -24,15 +30,16 @@ This article focuses on tasks that application programmers are likely to encount * Reading a ZIP file * Creating a temporary file or directory -The Java API supports many other tasks, which are explained in detail in the [Java I/O API tutorial](https://dev.java/learn/java-io/). +The Java API supports many other tasks, which are explained in detail in the [Java I/O API tutorial](id:api.javaio.overview). This article focuses on API improvements since Java 8. In particular: -* UTF-8 is the default for I/O since Java 18 ([JEP 400](https://openjdk.org/jeps/400)) -* The `java.nio.file.Files` class, which first appeared in Java 7, added useful methods in Java 8, 11, and 12 -* `java.io.InputStream` gained useful methods in Java 9, 11, and 12 -* The `java.io.File` and `java.io.BufferedReader` classes are now thoroughly obsolete, even though they appear frequently in web searches and AI chats. +* UTF-8 is the default for I/O since Java 18 (since [UTF-8 by Default](jep:400)) +* The [`java.nio.file.Files`](javadoc:Files) class, which first appeared in Java 7, added useful methods in Java 8, 11, and 12 +* [`java.io.InputStream`](javadoc:InputStream) gained useful methods in Java 9, 11, and 12 +* The [`java.io.File`](javadoc:File) and [`java.io.BufferedReader`](javadoc:BufferedReader) classes are now thoroughly obsolete, even though they appear frequently in web searches and AI chats. +  ## Reading Text Files You can read a text file into a string like this: @@ -41,13 +48,13 @@ You can read a text file into a string like this: String content = Files.readString(path); ``` -Here, `path` is an instance of `java.nio.Path`, obtained like this: +Here, `path` is an instance of [`java.nio.Path`](javadoc:Path), obtained like this: ```java var path = Path.of("/usr/share/dict/words"); ``` -Before Java 18, you were strongly encouraged to specify the character encoding with any file operations that read or write strings. Nowadays, by far the most common character encoding is UTF-8, but for backwards compatibility, Java used the "platform encoding", which can be a legacy encoding on Windows. To ensure portability, text I/O operations needed parameters `StandardCharsets.UTF_8`. This is no longer necessary. +Before Java 18, you were strongly encouraged to specify the character encoding with any file operations that read or write strings. Nowadays, by far the most common character encoding is UTF-8, but for backwards compatibility, Java used the "platform encoding", which can be a legacy encoding on Windows. To ensure portability, text I/O operations needed parameters [`StandardCharsets.UTF_8`](javadoc:StandardCharsets.UTF_8). This is no longer necessary. If you want the file as a sequence of lines, call @@ -55,7 +62,7 @@ If you want the file as a sequence of lines, call List lines = Files.readAllLines(path); ``` -If the file is large, process the lines lazily as a `Stream`: +If the file is large, process the lines lazily as a [`Stream`](javadoc:Stream): ```java try (Stream lines = Files.lines(path)) { @@ -63,20 +70,21 @@ try (Stream lines = Files.lines(path)) { } ``` -Also use `Files.lines` if you can naturally process lines with stream operations (such as `map`, `filter`). Note that the stream returned by `Files.lines` needs to be closed. To ensure that this happens, use a `try`-with-resources statement, as in the preceding code snippet. +Also use [`Files.lines`](javadoc:Files.lines(Path)) if you can naturally process lines with stream operations (such as [`map`](javadoc:Stream.map(Function)), [`filter`](javadoc:Stream.filter(Predicate))). Note that the stream returned by [`Files.lines`](javadoc:Files.lines(Path)) needs to be closed. To ensure that this happens, use a _try-with-resources_ statement, as in the preceding code snippet. -There is no longer a good reason to use the `readLine` method of `java.io.BufferedReader`. +There is no longer a good reason to use the [`readLine`](javadoc:BufferedReader.readLine()) method of [`java.io.BufferedReader`](javadoc:BufferedReader). -To split your input into something else than lines, use a `java.util.Scanner`. For example, here is how you can read words, separated by non-letters: +To split your input into something else than lines, use a [`java.util.Scanner`](javadoc:Scanner). For example, here is how you can read words, separated by non-letters: ```java Stream tokens = new Scanner(path).useDelimiter("\\PL+").tokens(); ``` -The `Scanner` class also has methods for reading numbers, but it is generally simpler to read the input as one string per line, or a single string, and then parse it. +The [`Scanner`](javadoc:Scanner) class also has methods for reading numbers, but it is generally simpler to read the input as one string per line, or a single string, and then parse it. -Be careful when parsing numbers from text files, since their format may be locale-dependent. For example, the input `100.000` is 100.0 in the US locale but 100000.0 in the German locale. Use `java.text.NumberFormat` for locale-specific parsing. Alternatively, you may be able to use `Integer.parseInt`/`Double.parseDouble`. +Be careful when parsing numbers from text files, since their format may be locale-dependent. For example, the input `100.000` is 100.0 in the US locale but 100000.0 in the German locale. Use [`java.text.NumberFormat`](javadoc:NumberFormat) for locale-specific parsing. Alternatively, you may be able to use [`Integer.parseInt`](javadoc:Integer.parseInt(String))/[`Double.parseDouble`](javadoc:Double.parseDouble(String)). +  ## Writing Text Files You can write a string to a text file with a single call: @@ -93,18 +101,18 @@ List lines = . . .; Files.write(path, lines); ``` -For more general output, use a `PrintWriter` if you want to use the `printf` method: +For more general output, use a [`PrintWriter`](javadoc:PrintWriter) if you want to use the [`printf`](javadoc:PrintWriter.printf()) method: ```java var writer = new PrintWriter(path.toFile()); writer.printf(locale, "Hello, %s, next year you'll be %d years old!%n", name, age + 1); ``` -Note that `printf` is locale-specific. When writing numbers, be sure to write them in the appropriate format. Instead of using `printf`, consider `java.text.NumberFormat` or `Integer.toString`/`Double.toString`. +Note that [`printf`](javadoc:PrintWriter.printf()) is locale-specific. When writing numbers, be sure to write them in the appropriate format. Instead of using [`printf`](javadoc:PrintWriter.printf()), consider [`java.text.NumberFormat`](javadoc:NumberFormat) or [`Integer.toString`](javadoc:Integer.toString())/[`Double.toString`](javadoc:Double.toString(double)). -Weirdly enough, as of Java 21, there is no `PrintWriter` constructor with a `Path` parameter. +Weirdly enough, as of Java 21, there is no [`PrintWriter`](javadoc:PrintWriter) constructor with a [`Path`](javadoc:Path) parameter. -If you don't use `printf`, you can use the `BufferedWriter` class and write strings with the `write` method. +If you don't use [`printf`](javadoc:PrintWriter.printf()), you can use the [`BufferedWriter`](javadoc:BufferedWriter) class and write strings with the [`write`](javadoc:BufferedWriter.write(int)) method. ```java var writer = Files.newBufferedWriter(path); @@ -114,11 +122,12 @@ writer.newLine(); Remember to close the `writer` when you are done. +  ## Reading From an Input Stream Perhaps the most common reason to use a stream is to read something from a web site. -If you need to set request headers or read response headers, use the `HttpClient`: +If you need to set request headers or read response headers, use the [`HttpClient`](javadoc:HttpClient): ```java HttpClient client = HttpClient.newBuilder().build(); @@ -164,19 +173,20 @@ Map result = JSON.std.mapFrom(url); Here is how to read the dog image from the preceding call: ```java -url = new URI(result.get("message").toString()).toURL(); -BufferedImage img = javax.imageio.ImageIO.read(url) +URL url = new URI(result.get("message").toString()).toURL(); +BufferedImage img = javax.imageio.ImageIO.read(url); ``` -This is better than passing an input stream to the `read` method, because the library can use additional information from the URL to determine the image type. +This is better than passing an input stream to the [`read`](javadoc:ImageIO.read(URL)) method, because the library can use additional information from the URL to determine the image type. +  ## The Files API -The `java.nio.file.Files` class provides a comprehensive set of file operations, such as creating, copying, moving, and deleting files and directories. The [File System Basics](https://dev.java/learn/java-io/file-system/) tutorial provides a thorough description. In this section, I highlight a few common tasks. +The [`java.nio.file.Files`](javadoc:Files) class provides a comprehensive set of file operations, such as creating, copying, moving, and deleting files and directories. The [File System Basics](id:api.javaio.file_sytem.intro) tutorial provides a thorough description. In this section, I highlight a few common tasks. ### Traversing Entries in Directories and Subdirectories -For most situations you can use one of two methods. The `Files.list` method visits all entries (files, subdirectories, symbolic links) of a directory. +For most situations you can use one of two methods. The [`Files.list`](javadoc:Files.list(Path)) method visits all entries (files, subdirectories, symbolic links) of a directory. ```java try (Stream entries = Files.list(pathToDirectory)) { @@ -184,9 +194,9 @@ try (Stream entries = Files.list(pathToDirectory)) { } ``` -Use a `try`-with-resources statement to ensure that the stream object, which keeps track of the iteration, will be closed. +Use a _try-with-resources_ statement to ensure that the stream object, which keeps track of the iteration, will be closed. -If you also want to visit the entries of descendant directories, instead use the method +If you also want to visit the entries of descendant directories, instead use the method [`Files.walk`](javadoc:Files.walk(Path)) ```java Stream entries = Files.walk(pathToDirectory); @@ -203,15 +213,15 @@ try (Stream entries = Files.walk(pathToDirectory)) { Here are the other methods for traversing directory entries: -* An overloaded version of `Files.walk` lets you limit the depth of the traversed tree. -* Two `Files.walkFileTree` methods provide more control over the iteration process, by notifying a `FileVisitor` when a directory is visited for the first and last time. This can be occasionally useful, in particularly for emptying and deleting a tree of directories. See the tutorial [Walking the File Tree](https://dev.java/learn/java-io/file-system/walking-tree) for details. Unless you need this control, use the simpler `Files.walk` method. -* The `Files.find` method is just like `Files.walk`, but you provide a filter that inspects each path and its `BasicFileAttributes`. This is slightly more efficient than reading the attributes separately for each file. -* Two `Files.newDirectoryStream` methods yields `DirectoryStream` instances, which can be used in enhanced `for` loops. There is no advantage over using `Files.list`. -* The legacy `File.list` or `File.listFiles` methods return file names or `File` objects. These are now obsolete. +* An overloaded version of [`Files.walk`](javadoc:Files.walk(Path,depth)) lets you limit the depth of the traversed tree. +* Two [`Files.walkFileTree`](javadoc:Files.walkFileTree(Path)) methods provide more control over the iteration process, by notifying a [`FileVisitor`](javadoc:FileVisitor) when a directory is visited for the first and last time. This can be occasionally useful, in particularly for emptying and deleting a tree of directories. See the tutorial [Walking the File Tree](id:api.javaio.file_sytem.walking_tree) for details. Unless you need this control, use the simpler [`Files.walk`](javadoc:Files.walk(Path)) method. +* The [`Files.find`](javadoc:Files.find(Path)) method is just like [`Files.walk`](javadoc:Files.walk(Path)), but you provide a filter that inspects each path and its [`BasicFileAttributes`](javadoc:BasicFileAttributes). This is slightly more efficient than reading the attributes separately for each file. +* Two [`Files.newDirectoryStream(Path)`](javadoc:Files.newDirectoryStream(Path)) methods yields [`DirectoryStream`](javadoc:DirectoryStream) instances, which can be used in enhanced `for` loops. There is no advantage over using [`Files.list`](javadoc:Files.list(Path)). +* The legacy [`File.list`](javadoc:File.list()) or [`File.listFiles`](javadoc:File.listFiles()) methods return file names or [`File`](javadoc:File) objects. These are now obsolete. ### Working with ZIP Files -Ever since Java 1.1, the `ZipInputStream` and `ZipOutputStream` classes provide an API for processing ZIP files. But the API is a bit clunky. Java 8 introduced a much nicer *ZIP file system*: +Ever since Java 1.1, the [`ZipInputStream`](javadoc:ZipInputStream) and [`ZipOutputStream`](javadoc:ZipOutputStream) classes provide an API for processing ZIP files. But the API is a bit clunky. Java 8 introduced a much nicer *ZIP file system*: ```java try (FileSystem fs = FileSystems.newFileSystem(pathToZipFile)) { @@ -219,9 +229,9 @@ try (FileSystem fs = FileSystems.newFileSystem(pathToZipFile)) { } ``` -The `try`-with-resources statement ensures that the `close` method is called after the ZIP file operations. That method updates the ZIP file to reflect any changes in the file system. +The _try-with-resources_ statement ensures that the [`close`](javadoc:AutoCloseable.close()) method is called after the ZIP file operations. That method updates the ZIP file to reflect any changes in the file system. -You can then use the methods of the `Files` class. Here we get a list of all files in the ZIP file: +You can then use the methods of the [`Files`](javadoc:Files) class. Here we get a list of all files in the ZIP file: ```java try (Stream entries = Files.walk(fs.getPath("/"))) { @@ -229,35 +239,36 @@ try (Stream entries = Files.walk(fs.getPath("/"))) { } ``` -To read the file contents, just use `Files.readString` or `Files.readAllBytes`: +To read the file contents, just use [`Files.readString`](javadoc:Files.readString(Path)) or [`Files.readAllBytes`](javadoc:Files.readAllBytes(Path)): ```java String contents = Files.readString(fs.getPath("/LICENSE")); ``` -You can remove files with `Files.delete`. To add or replace files, simply use `Files.writeString` or `Files.write`. +You can remove files with [`Files.delete`](javadoc:Files.delete()). To add or replace files, simply use [`Files.writeString`](javadoc:Files.writeString()) or [`Files.write`](javadoc:Files.write()). ### Creating Temporary Files and Directories Fairly often, I need to collect user input, produce files, and run an external process. Then I use temporary files, which are gone after the next reboot, or a temporary directory that I erase after the process has completed. -The calls +I use the two methods [`Files.createTempFile`](javadoc:Files.createTempFile(String,String,FileAttribute)) and [`Files.createTempDirectory`](javadoc:Files.createTempDirectory(Path,String,FileAttribute...)) for that. ```java Path filePath = Files.createTempFile("myapp", ".txt"); Path dirPath = Files.createTempDirectory("myapp"); ``` -create a temporary file or directory in a suitable location (`/tmp` in Linux) with the given prefix and, for a file, suffix. +This creates a temporary file or directory in a suitable location (`/tmp` in Linux) with the given prefix and, for a file, suffix. +  ## Conclusion Web searches and AI chats can suggest needlessly complex code for common I/O operations. There are often better alternatives: 1. You don't need a loop to read or write strings or byte arrays. 2. You may not even need a stream, reader or writer. -3. Become familiar with the `Files` methods for creating, copying, moving, and deleting files and directories. -4. Use `Files.list` or `Files.walk` to traverse directory entries. +3. Become familiar with the [`Files`](javadoc:Files) methods for creating, copying, moving, and deleting files and directories. +4. Use [`Files.list`](javadoc:Files.list(Path)) or [`Files.walk`](javadoc:Files.walk(Path)) to traverse directory entries. 5. Use a ZIP file system for processing ZIP files. -6. Stay away from the legacy `File` class. +6. Stay away from the legacy [`File`](javadoc:File) class. diff --git a/gulpfile.js b/gulpfile.js index 6b96828..81d9800 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,6 +27,7 @@ for (const author of authorsArray) { var javadoc = require('./app/data/javadoc.json') var javafxdoc = require('./app/data/javafxdoc.json') +var jep = require('./app/data/jep.json') function getAuthorID(name) { @@ -251,7 +252,37 @@ function pages() { } } + if (href.includes("jep:")) { + let jep_id = href.match(/jep:([\d]+)/); + if (jep_id != null) { + if (jep[jep_id[1]] == undefined) { + console.log("JEP " + jep_id[1] + " is not defined"); + } else { + processedHref = jep["root"] + jep_id[1]; + let anchor = href.match(/#[\w|-]+/); + if (anchor) { + processedHref = processedHref + anchor; + } + if (jep[jep_id[1]]["status"] == "preview" && jep[jep_id[1]]["version"] != javadoc["current_release"]) { + console.log( + "Current release is " + javadoc["current_release"] + ", JEP " + jep_id[1] + + " is a preview of " + jep[jep_id[1]]["version"] + " and still referenced"); + } + if (jep[jep_id[1]]["status"] == "incubator" && jep[jep_id[1]]["version"] != javadoc["current_release"]) { + console.log( + "Current release is " + javadoc["current_release"] + ", JEP " + jep_id[1] + + " is an incubator of " + jep[jep_id[1]]["version"] + " and still referenced"); + } + if (jep[jep_id[1]]["title"]) { + text = "JEP " + jep_id[1] + ": " + jep[jep_id[1]]["title"]; + } else { + text = "JEP " + jep_id[1] + ": " + jep[jep_id[1]]; + } + return `${text}`; + } + } + } if (title) { link = `${text}`; From 463e48d534f17878ac99354afc4e8062606e907c Mon Sep 17 00:00:00 2001 From: abhishekabramaina <90707533+abhishekabramaina@users.noreply.github.com> Date: Thu, 5 Dec 2024 00:22:25 +0530 Subject: [PATCH 23/26] fixed the typo described in issue #96 (#129) --- .../03_converting_foreach_with_if.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/03_refactoring_to_functional_style/03_converting_foreach_with_if.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/03_refactoring_to_functional_style/03_converting_foreach_with_if.md index 4a20eb5..3dcff3d 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/03_refactoring_to_functional_style/03_converting_foreach_with_if.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/03_refactoring_to_functional_style/03_converting_foreach_with_if.md @@ -76,7 +76,7 @@ for(String name: names) { For the functional style, the `filter` method of `Stream` becomes a direct replacement of the imperative style `if`. The `filter` method will allow an element in the collection to pass through to the next stage in the functional pipeline if the predicate, passed in as a lambda, to the `filter()` method evaluates to `true`; otherwise, the value is discarded from further processing. -Let's conver the previous code to functional style: +Let's convert the previous code to functional style: ```java List names = List.of("Jack", "Paula", "Kate", "Peter"); From 3ee75ddfcafb1e7078b8ae79cbd7bc7e42d63d94 Mon Sep 17 00:00:00 2001 From: Marit van Dijk Date: Mon, 16 Dec 2024 09:23:49 +0100 Subject: [PATCH 24/26] Fix incorrect shortcut for Toggle Breakpoint. (#131) * Fix incorrect shortcut for Toggle Breakpoint. Some additional small grammar fixes. * Fix incorrect shortcut for Toggle Breakpoint. Some additional small grammar fixes. --- .../02_building-with-intellij-idea.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/pages/learn/01_tutorial/01_your-first-java-app/02_building-with-intellij-idea.md b/app/pages/learn/01_tutorial/01_your-first-java-app/02_building-with-intellij-idea.md index cae4f20..18d4958 100644 --- a/app/pages/learn/01_tutorial/01_your-first-java-app/02_building-with-intellij-idea.md +++ b/app/pages/learn/01_tutorial/01_your-first-java-app/02_building-with-intellij-idea.md @@ -31,7 +31,7 @@ author: ["MaritvanDijk"]   ## Overview -An IDE (Integrated Development Environment) allows you to quickly create applications, by combining a source-code editor with the ability to compile and run your code, as well as integration with build, test and debug tools, version control systems, and so on. Finally, an IDE will let you search and navigate your codebase in ways your file system won’t. +An IDE (Integrated Development Environment) allows you to quickly create applications by combining a source-code editor with the ability to compile and run your code, as well as integration with build, test and debug tools, version control systems, and so on. Finally, an IDE will let you search and navigate your codebase in ways your file system won’t. One of the [most widely used integrated development environments (IDEs)](https://www.jetbrains.com/lp/devecosystem-2023/java/#java_ide) for Java is IntelliJ IDEA. Its user-friendly interface, rich feature set, and vast ecosystem make it an ideal environment for beginners to learn and grow as developers. In this tutorial you’ll learn how to use some of its features to simplify your development process and accelerate your learning curve with Java programming. @@ -45,7 +45,7 @@ Note that IntelliJ IDEA is available in two editions: For this tutorial, you can download the Community Edition. For more information on installing IntelliJ IDEA on your OS, see [the documentation](https://www.jetbrains.com/help/idea/installation-guide.html#standalone). -When you launch IntelliJ IDEA for the first time, you’ll see the **Welcome** screen. From here, you create a new project, open an existing project, or get a project from a version control system (like GitHub). +When you launch IntelliJ IDEA for the first time, you’ll see the **Welcome** screen. From here, you can create a new project, open an existing project, or get a project from a version control system (like GitHub). [![Welcome screen](/assets/images/intellij-idea/welcome-screen.png)](/assets/images/intellij-idea/welcome-screen.png) @@ -58,7 +58,7 @@ We can create a new project from the **Welcome** screen, or we can go to **File [![New Project menu](/assets/images/intellij-idea/new-project-menu.png)](/assets/images/intellij-idea/new-project-menu.png) -In the **New Project** wizard, make sure that **Java** is selected on the left hand side, and give your project a name (for example, `java-demo`). +In the **New Project** wizard, make sure that **Java** is selected on the left-hand side, and give your project a name (for example, `java-demo`). Next, we'll select a **Build system**. IntelliJ IDEA supports both Maven and Gradle; the most used build systems for Java. A build tool, like Maven or Gradle, helps you to build your project, and manage any dependencies (like additional libraries) that you want to use in your Java code. Using a build tool will also make it easier to share your application and build it on a different machine. If you don't want to use either, you can use the IntelliJ build system. In this tutorial, let’s create a Maven project. [![New Project](/assets/images/intellij-idea/new-project.png)](/assets/images/intellij-idea/new-project.png) @@ -133,7 +133,7 @@ IntelliJ IDEA will manage the formatting of your code as you write it. If needed A major benefit of using an IDE is that you can directly run your code without having to first manually compile it on the command line. -You can run the `HelloWorld` application directly from the editor, by clicking the green run button in the gutter near the class declaration, or using the shortcut **⌃⇧R** (on macOS) or **Ctrl+Shift+F10** (on Windows/Linux). +You can run the `HelloWorld` application directly from the editor, by clicking the green Run button in the gutter near the class declaration, or using the shortcut **⌃⇧R** (on macOS) or **Ctrl+Shift+F10** (on Windows/Linux). Alternatively, we can run our application using the green Run button in the top right corner, or using the shortcut **⌃R** (on macOS) or **Ctrl+F10** (on Windows/Linux) to run the latest file. @@ -145,7 +145,7 @@ To edit your run configurations, select the configuration in the run/debug confi [![Edit Configurations](/assets/images/intellij-idea/edit-configurations.png)](/assets/images/intellij-idea/edit-configurations.png) -The popup **Run/Debug Configurations** appears and there you can modify JVM options, add program arguments and many more. +The popup **Run/Debug Configurations** appears, and there you can modify JVM options, add program arguments, and many more. [![Run / Debug Configuration](/assets/images/intellij-idea/run-config.png)](/assets/images/intellij-idea/run-config.png) @@ -173,7 +173,7 @@ We can select a **Testing library** in the **Create test** popup. [![Create test](/assets/images/intellij-idea/create-test.png)](/assets/images/intellij-idea/create-test.png) -IntelliJ IDEA supports multiple testing libraries, including [JUnit 5](https://junit.org/junit5/), which is the [most used testing library for Java developers](https://www.jetbrains.com/lp/devecosystem-2023/java/#java_unittesting). If JUnit 5 is not part of your project yet, IntelliJ IDEA will note “JUnit5 library not found in the module”. Click **Fix** to have IntelliJ IDEA fix this for you. +IntelliJ IDEA supports multiple testing libraries, including [JUnit 5](https://junit.org/junit5/), which is the [most used testing library for Java developers](https://www.jetbrains.com/lp/devecosystem-2023/java/#java_unittesting). If JUnit 5 is not part of your project yet, IntelliJ IDEA will note “JUnit5 library not found in the module.” Click **Fix** to have IntelliJ IDEA fix this for you. Note that the JUnit 5 dependency `junit-jupiter` is added to the `pom.xml` in the `` section. To make sure the dependencies work correctly in your project, **Load Maven Changes** by clicking the popup in the top right corner, or using the shortcut **⇧⌘I** (on macOS) or **Ctrl+Shift+O** (on Windows/Linux). @@ -212,16 +212,16 @@ public class CalculatorTest { ``` In our test class, we can select **Run All Tests** (**⌃⇧R** on macOS or **Ctrl+Shift+F10** on Windows/Linux). -In our example, we see that the second tests fails. We expected to get the value `0` as the average of an empty array, but got `NaN` (not a number) instead. Let's find out why, using the debugger. +In our example, we see that the second test fails. We expected to get the value `0` as the average of an empty array, but got `NaN` (not a number) instead. Let's find out why, using the debugger.   ## Debugging We might want to see how our code runs, either to help us understand how it works and/or when we need to fix a bug or failing test, like the one above. We can run our code through the [debugger](https://www.jetbrains.com/help/idea/debugging-code.html) to see the state of our variables at different times, and the call stack - the order in which methods are called when the program executes. To do so, we must first add a [breakpoint](https://www.jetbrains.com/help/idea/using-breakpoints.html) to the code. -To add a breakpoint, click the gutter at the line of code where you want execution to stop. Alternatively, place the caret at the line and press **⌃F8** (on macOS) or **Ctrl+F8** (on Windows/Linux). We can run our test or application using the **Debug** option; either by right-clicking the **Run** button in the gutter and selecting the **Debug** option from the list, or by selecting the **Debug** button at the top right. +To add a breakpoint, click the gutter at the line of code where you want execution to stop. Alternatively, place the caret at the line and press **⌘F8** (on macOS) or **Ctrl+F8** (on Windows/Linux). We can run our test or application using the **Debug** option; either by right-clicking the **Run** button in the gutter and selecting the **Debug** option from the list, or by selecting the **Debug** button at the top right. -Execution will stop at the breakpoint, so we can investigate the state of our application. We can see the current values of variables and objects. We can evaluate an expression, to see its current value and look at more details. We can even change the expressions to evaluate different results. We can continue execution by either stepping into a method to see what happens inside a called method (using the shortcut **F7**, or the corresponding button in the **Debug** tool window) or stepping over a line to go to the next line even if a method is called (using the shortcut **F8**, or the corresponding button in the **Debug** tool window), depending on what we’re interested in. Finally, we can resume the program to finish the execution of the test. +Execution will stop at the breakpoint, so we can investigate the state of our application. We can see the current values of variables and objects. We can evaluate an expression to see its current value and look at more details. We can even change the expressions to evaluate different results. We can continue execution by either stepping into a method to see what happens inside a called method (using the shortcut **F7**, or the corresponding button in the **Debug** tool window) or stepping over a line to go to the next line even if a method is called (using the shortcut **F8**, or the corresponding button in the **Debug** tool window), depending on what we’re interested in. Finally, we can resume the program to finish the execution of the test. Let's debug the failing test from the previous section. In the code, place a breakpoint on line 4. Run the failing test through the debugger. Step over the code until you get to line 8, and observe the values of the variables. When we get to line 8, select `sum / numbers.length`, right-click to open the context menu and select **Evaluate Expression**. Press **Enter** to evaluate the selected expression. We see that `sum / numbers.length` results in a `java.lang.ArithmeticException: / by zero`. The empty array has a length of `0` and Java does not allow dividing by zero. When we evaluate `(double) sum / numbers.length` we get the result `NaN`. We expected `0`, so our test fails. @@ -269,12 +269,12 @@ Pull up the refactoring menu to see what is possible, using the shortcut **⌃T* We can add documentation to our code. IntelliJ IDEA provides completion for documentation comments, which is enabled by default. Type `/**` before a declaration and press **Enter**. IntelliJ IDEA auto-completes the documentation comment for you. -IntelliJ IDEA provides a way for you to easily understand and read JavaDoc comments by selecting _Reader Mode_. **Toggle Reader Mode** in the editor using **^⌥Q** (on macOS) or **Ctrl+Alt+Q** (on Windows/Linux). Right-click the icon in the gutter to select **Render All Doc Comments** if you want all comments to show in reader mode. +IntelliJ IDEA provides a way for you to easily understand and read Javadoc comments by selecting _Reader Mode_. **Toggle Rendered View** in the editor using **^⌥Q** (on macOS) or **Ctrl+Alt+Q** (on Windows/Linux). Right-click the icon in the gutter to select **Render All Doc Comments** if you want all comments to show in reader mode.   ## Searching and navigating -IntelliJ IDEA also helps us by providing ways to navigate around our codebase, for example by going backwards and forwards between files, finding usages and declarations, finding interfaces and their implementations, viewing recently opened files and location, or even opening a window by name. +IntelliJ IDEA also helps us by providing ways to navigate around our codebase, for example, by going backwards and forwards between files, finding usages and declarations, finding interfaces and their implementations, viewing recently opened files and location, or even opening a window by name. One popular way to search is [Search Everywhere](https://www.jetbrains.com/help/idea/searching-everywhere.html) (using **Shift** twice). Search everywhere allows you to search your project files and directories, as well as your project settings and IntelliJ IDEA settings. From 457e0dfadf952a3384cfd9d6918bf6f1ab820bcc Mon Sep 17 00:00:00 2001 From: dan1st Date: Fri, 24 Oct 2025 12:49:54 +0200 Subject: [PATCH 25/26] fix wrong JDK version for record introduction (#138) fixes #137 --- .../04_classes_objects/01_enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md index 129676c..f00eafe 100644 --- a/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md +++ b/app/pages/learn/01_tutorial/03_getting-to-know-the-language/04_classes_objects/01_enums.md @@ -212,6 +212,6 @@ and reading these configuration files in the program in cases like this.   ## Conclusion -Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, maintainable and works well with other modern Java features like [switch expressions](id:lang.classes-objects.switch-expression). Another special class is the Record class introduced in Java 19. Visit our [records tutorial](id:lang.records) to learn more. +Enums provide a simple and safe way of representing a fixed set of constants while keeping most of the flexibilities of classes. They are a special type of class that can be used to write code that is elegant, readable, maintainable and works well with other modern Java features like [switch expressions](id:lang.classes-objects.switch-expression). Another special class is the Record class introduced in JDK 16. Visit our [records tutorial](id:lang.records) to learn more. To learn more about enums, visit the [`java.lang.Enum`](javadoc:Enum) javadoc. \ No newline at end of file From 5bde67032f9c9d8230a3700d2d700da97bd595d2 Mon Sep 17 00:00:00 2001 From: dan1st Date: Fri, 24 Oct 2025 12:50:33 +0200 Subject: [PATCH 26/26] minor changes to modern IO article (#121) --- .../04_mastering-the-api/02_modern_io/01_modern_io.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md b/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md index 5bd0496..1dd5dfd 100644 --- a/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md +++ b/app/pages/learn/01_tutorial/04_mastering-the-api/02_modern_io/01_modern_io.md @@ -14,6 +14,7 @@ toc: - The Files API {the-files-api} - Conclusion {conclusion} last_update: 2024-04-24 +last_review: 2024-08-05 description: "This article focuses on tasks that application programmers are likely to encounter, particularly in web applications, such as reading and writing text files, reading text, images, JSON from the web, and more." author: ["CayHorstmann"] --- @@ -155,8 +156,9 @@ String result = new String(bytes); Or transfer the data to an output stream: ```java -OutputStream out = Files.newOutputStream(path); -in.transferTo(out); +try(OutputStream out = Files.newOutputStream(path)) { + in.transferTo(out); +} ``` Note that no loop is required if you simply want to read all bytes of an input stream. @@ -216,7 +218,7 @@ Here are the other methods for traversing directory entries: * An overloaded version of [`Files.walk`](javadoc:Files.walk(Path,depth)) lets you limit the depth of the traversed tree. * Two [`Files.walkFileTree`](javadoc:Files.walkFileTree(Path)) methods provide more control over the iteration process, by notifying a [`FileVisitor`](javadoc:FileVisitor) when a directory is visited for the first and last time. This can be occasionally useful, in particularly for emptying and deleting a tree of directories. See the tutorial [Walking the File Tree](id:api.javaio.file_sytem.walking_tree) for details. Unless you need this control, use the simpler [`Files.walk`](javadoc:Files.walk(Path)) method. * The [`Files.find`](javadoc:Files.find(Path)) method is just like [`Files.walk`](javadoc:Files.walk(Path)), but you provide a filter that inspects each path and its [`BasicFileAttributes`](javadoc:BasicFileAttributes). This is slightly more efficient than reading the attributes separately for each file. -* Two [`Files.newDirectoryStream(Path)`](javadoc:Files.newDirectoryStream(Path)) methods yields [`DirectoryStream`](javadoc:DirectoryStream) instances, which can be used in enhanced `for` loops. There is no advantage over using [`Files.list`](javadoc:Files.list(Path)). +* Two [`Files.newDirectoryStream(Path)`](javadoc:Files.newDirectoryStream(Path)) methods yield [`DirectoryStream`](javadoc:DirectoryStream) instances, which can be used in enhanced `for` loops. There is no advantage over using [`Files.list`](javadoc:Files.list(Path)). * The legacy [`File.list`](javadoc:File.list()) or [`File.listFiles`](javadoc:File.listFiles()) methods return file names or [`File`](javadoc:File) objects. These are now obsolete. ### Working with ZIP Files